feat: Enhance chat room registration flow and message visibility
- Increase public message limit from 10 to 50 for better context - Add registration prompt for users after viewing 50 messages - Implement dynamic signup modal trigger in chat room - Update language meta tags and HTML lang attribute to French - Modify aim-login component to support initial tab state
- commit
- ac75bce1324602fb6ee82e54704b876ba5c02349
- author
- Kit · · 2025-02-17 07:26 UTC
- parent
- 596c40c
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
5 files · +78 -8
M src/app.html +1 -1
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | <!doctype html> | |
| 2 | - | <html lang="en"> | |
| 2 | + | <html lang="fr"> | |
| 3 | 3 | <head> | |
| 4 | 4 | <meta charset="utf-8" /> | |
| 5 | 5 | <link rel="icon" href="%sveltekit.assets%/favicon.png" /> | |
| @@ -1,5 +1,5 @@ | |||||
| 1 | <!doctype html> | 1 | <!doctype html> | ||
| 2 | - | <html lang="en"> | 2 | + | <html lang="fr"> |
| 3 | <head> | 3 | <head> | ||
| 4 | <meta charset="utf-8" /> | 4 | <meta charset="utf-8" /> | ||
| 5 | <link rel="icon" href="%sveltekit.assets%/favicon.png" /> | 5 | <link rel="icon" href="%sveltekit.assets%/favicon.png" /> | ||
M src/lib/components/aim-login.svelte +10 -2
| @@ -7,9 +7,17 @@ | |||
| 7 | 7 | import { chatState } from '$lib/states/chat.svelte'; | |
| 8 | 8 | import Turnstile from './turnstile.svelte'; | |
| 9 | 9 | ||
| 10 | - | let { onLoginSuccess, showAuth = $bindable() }: { onLoginSuccess?: (user: User) => void, showAuth:Boolean } = $props(); | |
| 10 | + | let { | |
| 11 | + | onLoginSuccess, | |
| 12 | + | showAuth = $bindable(), | |
| 13 | + | activeTab: initialTab = 'signin' | |
| 14 | + | }: { | |
| 15 | + | onLoginSuccess?: (user: User) => void, | |
| 16 | + | showAuth: Boolean, | |
| 17 | + | activeTab?: 'signin' | 'signup' | |
| 18 | + | } = $props(); | |
| 11 | 19 | ||
| 12 | - | let activeTab = $state('signin'); | |
| 20 | + | let activeTab = $state(initialTab); | |
| 13 | 21 | ||
| 14 | 22 | // Sign In state | |
| 15 | 23 | let siUsername = $state(''); | |
| @@ -7,9 +7,17 @@ | |||||
| 7 | import { chatState } from '$lib/states/chat.svelte'; | 7 | import { chatState } from '$lib/states/chat.svelte'; | ||
| 8 | import Turnstile from './turnstile.svelte'; | 8 | import Turnstile from './turnstile.svelte'; | ||
| 9 | 9 | ||||
| 10 | - | let { onLoginSuccess, showAuth = $bindable() }: { onLoginSuccess?: (user: User) => void, showAuth:Boolean } = $props(); | 10 | + | let { |
| 11 | + | onLoginSuccess, | |||
| 12 | + | showAuth = $bindable(), | |||
| 13 | + | activeTab: initialTab = 'signin' | |||
| 14 | + | }: { | |||
| 15 | + | onLoginSuccess?: (user: User) => void, | |||
| 16 | + | showAuth: Boolean, | |||
| 17 | + | activeTab?: 'signin' | 'signup' | |||
| 18 | + | } = $props(); | |||
| 11 | 19 | ||||
| 12 | - | let activeTab = $state('signin'); | 20 | + | let activeTab = $state(initialTab); |
| 13 | 21 | ||||
| 14 | // Sign In state | 22 | // Sign In state | ||
| 15 | let siUsername = $state(''); | 23 | let siUsername = $state(''); | ||
M src/lib/components/chat-room.svelte +64 -1
| @@ -9,6 +9,7 @@ import { maximizable } from '$lib/actions/maximizable'; | |||
| 9 | 9 | import LoadingButton from './ui/button-loading.svelte'; | |
| 10 | 10 | import Tooltip from './ui/tooltip.svelte'; | |
| 11 | 11 | import LoadingDots from './ui/loading-dots.svelte'; | |
| 12 | + | import AimLogin from './aim-login.svelte'; | |
| 12 | 13 | import { formatFrenchDateTime, formatFrenchRelativeTimeSafe } from '$lib/utils/date-format'; | |
| 13 | 14 | ||
| 14 | 15 | // Initialize with default values for SSR | |
| @@ -21,6 +22,7 @@ let currentMessage = $state(''); | |||
| 21 | 22 | let showUserList = $state(false); | |
| 22 | 23 | let isMaximized = $state(false); | |
| 23 | 24 | let isInitialLoading = $state(true); | |
| 25 | + | let showAuth = $state(false); | |
| 24 | 26 | ||
| 25 | 27 | // Rate limiting state | |
| 26 | 28 | let cooldownEndTime = $state<number | null>(null); | |
| @@ -74,10 +76,13 @@ let visibleMessages = $derived((() => { | |||
| 74 | 76 | const messageCount = messages.length; | |
| 75 | 77 | const result: EnrichedMessage[] = isLoggedIn | |
| 76 | 78 | ? messages | |
| 77 | - | : messages.slice(Math.max(0, messageCount - 10)); | |
| 79 | + | : messages.slice(Math.max(0, messageCount - 50)); | |
| 78 | 80 | return result; | |
| 79 | 81 | })()); | |
| 80 | 82 | ||
| 83 | + | // Add a derived state to check if we're showing the registration prompt | |
| 84 | + | let showRegistrationPrompt = $derived(!currentUser && messages.length > 50); | |
| 85 | + | ||
| 81 | 86 | // Add SSE error state | |
| 82 | 87 | let sseError = $state<string | null>(null); | |
| 83 | 88 | let sseRetryAfter = $state<number | null>(null); | |
| @@ -372,6 +377,10 @@ $effect(() => { | |||
| 372 | 377 | ||
| 373 | 378 | // Add derived state for total users count | |
| 374 | 379 | let totalUsers = $derived(onlineUsers.length); | |
| 380 | + | ||
| 381 | + | function openSignup() { | |
| 382 | + | showAuth = true; | |
| 383 | + | } | |
| 375 | 384 | </script> | |
| 376 | 385 | ||
| 377 | 386 | {#if showChatRoom} | |
| @@ -454,6 +463,11 @@ let totalUsers = $derived(onlineUsers.length); | |||
| 454 | 463 | <LoadingDots text="Chargement" /> | |
| 455 | 464 | </div> | |
| 456 | 465 | {/if} | |
| 466 | + | {#if showRegistrationPrompt} | |
| 467 | + | <div class="registration-prompt"> | |
| 468 | + | <p>👋 <button class="link-button" onclick={openSignup}>Inscris-toi</button> pour lire le reste du chat !</p> | |
| 469 | + | </div> | |
| 470 | + | {/if} | |
| 457 | 471 | {#each visibleMessages as message (message.id)} | |
| 458 | 472 | <div class="message {message.type} text"> | |
| 459 | 473 | {#if message.type === 'emote'} | |
| @@ -521,6 +535,13 @@ let totalUsers = $derived(onlineUsers.length); | |||
| 521 | 535 | </div> | |
| 522 | 536 | {/if} | |
| 523 | 537 | ||
| 538 | + | {#if showAuth} | |
| 539 | + | <AimLogin | |
| 540 | + | bind:showAuth | |
| 541 | + | activeTab={'signup' as const} | |
| 542 | + | /> | |
| 543 | + | {/if} | |
| 544 | + | ||
| 524 | 545 | <style> | |
| 525 | 546 | .chat-area, .users-list, input { | |
| 526 | 547 | font-size: 1rem; | |
| @@ -806,4 +827,46 @@ let totalUsers = $derived(onlineUsers.length); | |||
| 806 | 827 | margin-bottom: 1rem; | |
| 807 | 828 | border-radius: 4px; | |
| 808 | 829 | } | |
| 830 | + | ||
| 831 | + | .registration-prompt { | |
| 832 | + | background: #fff3e0; | |
| 833 | + | color: #e65100; | |
| 834 | + | padding: 1rem; | |
| 835 | + | margin-bottom: 1rem; | |
| 836 | + | border-radius: 4px; | |
| 837 | + | text-align: center; | |
| 838 | + | font-size: 0.95rem; | |
| 839 | + | border: 1px solid #ffe0b2; | |
| 840 | + | animation: fadeIn 0.3s ease-out; | |
| 841 | + | } | |
| 842 | + | ||
| 843 | + | .registration-prompt p { | |
| 844 | + | margin: 0; | |
| 845 | + | } | |
| 846 | + | ||
| 847 | + | @keyframes fadeIn { | |
| 848 | + | from { | |
| 849 | + | opacity: 0; | |
| 850 | + | transform: translateY(-10px); | |
| 851 | + | } | |
| 852 | + | to { | |
| 853 | + | opacity: 1; | |
| 854 | + | transform: translateY(0); | |
| 855 | + | } | |
| 856 | + | } | |
| 857 | + | ||
| 858 | + | .registration-prompt .link-button { | |
| 859 | + | color: #e65100; | |
| 860 | + | text-decoration: underline; | |
| 861 | + | cursor: pointer; | |
| 862 | + | background: none; | |
| 863 | + | border: none; | |
| 864 | + | padding: 0; | |
| 865 | + | font: inherit; | |
| 866 | + | } | |
| 867 | + | ||
| 868 | + | .registration-prompt .link-button:hover { | |
| 869 | + | color: #ef6c00; | |
| 870 | + | text-decoration: none; | |
| 871 | + | } | |
| 809 | 872 | </style>⏎̸ | |
| @@ -9,6 +9,7 @@ import { maximizable } from '$lib/actions/maximizable'; | |||||
| 9 | import LoadingButton from './ui/button-loading.svelte'; | 9 | import LoadingButton from './ui/button-loading.svelte'; | ||
| 10 | import Tooltip from './ui/tooltip.svelte'; | 10 | import Tooltip from './ui/tooltip.svelte'; | ||
| 11 | import LoadingDots from './ui/loading-dots.svelte'; | 11 | import LoadingDots from './ui/loading-dots.svelte'; | ||
| 12 | + | import AimLogin from './aim-login.svelte'; | |||
| 12 | import { formatFrenchDateTime, formatFrenchRelativeTimeSafe } from '$lib/utils/date-format'; | 13 | import { formatFrenchDateTime, formatFrenchRelativeTimeSafe } from '$lib/utils/date-format'; | ||
| 13 | 14 | ||||
| 14 | // Initialize with default values for SSR | 15 | // Initialize with default values for SSR | ||
| @@ -21,6 +22,7 @@ let currentMessage = $state(''); | |||||
| 21 | let showUserList = $state(false); | 22 | let showUserList = $state(false); | ||
| 22 | let isMaximized = $state(false); | 23 | let isMaximized = $state(false); | ||
| 23 | let isInitialLoading = $state(true); | 24 | let isInitialLoading = $state(true); | ||
| 25 | + | let showAuth = $state(false); | |||
| 24 | 26 | ||||
| 25 | // Rate limiting state | 27 | // Rate limiting state | ||
| 26 | let cooldownEndTime = $state<number | null>(null); | 28 | let cooldownEndTime = $state<number | null>(null); | ||
| @@ -74,10 +76,13 @@ let visibleMessages = $derived((() => { | |||||
| 74 | const messageCount = messages.length; | 76 | const messageCount = messages.length; | ||
| 75 | const result: EnrichedMessage[] = isLoggedIn | 77 | const result: EnrichedMessage[] = isLoggedIn | ||
| 76 | ? messages | 78 | ? messages | ||
| 77 | - | : messages.slice(Math.max(0, messageCount - 10)); | 79 | + | : messages.slice(Math.max(0, messageCount - 50)); |
| 78 | return result; | 80 | return result; | ||
| 79 | })()); | 81 | })()); | ||
| 80 | 82 | ||||
| 83 | + | // Add a derived state to check if we're showing the registration prompt | |||
| 84 | + | let showRegistrationPrompt = $derived(!currentUser && messages.length > 50); | |||
| 85 | + | ||||
| 81 | // Add SSE error state | 86 | // Add SSE error state | ||
| 82 | let sseError = $state<string | null>(null); | 87 | let sseError = $state<string | null>(null); | ||
| 83 | let sseRetryAfter = $state<number | null>(null); | 88 | let sseRetryAfter = $state<number | null>(null); | ||
| @@ -372,6 +377,10 @@ $effect(() => { | |||||
| 372 | 377 | ||||
| 373 | // Add derived state for total users count | 378 | // Add derived state for total users count | ||
| 374 | let totalUsers = $derived(onlineUsers.length); | 379 | let totalUsers = $derived(onlineUsers.length); | ||
| 380 | + | ||||
| 381 | + | function openSignup() { | |||
| 382 | + | showAuth = true; | |||
| 383 | + | } | |||
| 375 | </script> | 384 | </script> | ||
| 376 | 385 | ||||
| 377 | {#if showChatRoom} | 386 | {#if showChatRoom} | ||
| @@ -454,6 +463,11 @@ let totalUsers = $derived(onlineUsers.length); | |||||
| 454 | <LoadingDots text="Chargement" /> | 463 | <LoadingDots text="Chargement" /> | ||
| 455 | </div> | 464 | </div> | ||
| 456 | {/if} | 465 | {/if} | ||
| 466 | + | {#if showRegistrationPrompt} | |||
| 467 | + | <div class="registration-prompt"> | |||
| 468 | + | <p>👋 <button class="link-button" onclick={openSignup}>Inscris-toi</button> pour lire le reste du chat !</p> | |||
| 469 | + | </div> | |||
| 470 | + | {/if} | |||
| 457 | {#each visibleMessages as message (message.id)} | 471 | {#each visibleMessages as message (message.id)} | ||
| 458 | <div class="message {message.type} text"> | 472 | <div class="message {message.type} text"> | ||
| 459 | {#if message.type === 'emote'} | 473 | {#if message.type === 'emote'} | ||
| @@ -521,6 +535,13 @@ let totalUsers = $derived(onlineUsers.length); | |||||
| 521 | </div> | 535 | </div> | ||
| 522 | {/if} | 536 | {/if} | ||
| 523 | 537 | ||||
| 538 | + | {#if showAuth} | |||
| 539 | + | <AimLogin | |||
| 540 | + | bind:showAuth | |||
| 541 | + | activeTab={'signup' as const} | |||
| 542 | + | /> | |||
| 543 | + | {/if} | |||
| 544 | + | ||||
| 524 | <style> | 545 | <style> | ||
| 525 | .chat-area, .users-list, input { | 546 | .chat-area, .users-list, input { | ||
| 526 | font-size: 1rem; | 547 | font-size: 1rem; | ||
| @@ -806,4 +827,46 @@ let totalUsers = $derived(onlineUsers.length); | |||||
| 806 | margin-bottom: 1rem; | 827 | margin-bottom: 1rem; | ||
| 807 | border-radius: 4px; | 828 | border-radius: 4px; | ||
| 808 | } | 829 | } | ||
| 830 | + | ||||
| 831 | + | .registration-prompt { | |||
| 832 | + | background: #fff3e0; | |||
| 833 | + | color: #e65100; | |||
| 834 | + | padding: 1rem; | |||
| 835 | + | margin-bottom: 1rem; | |||
| 836 | + | border-radius: 4px; | |||
| 837 | + | text-align: center; | |||
| 838 | + | font-size: 0.95rem; | |||
| 839 | + | border: 1px solid #ffe0b2; | |||
| 840 | + | animation: fadeIn 0.3s ease-out; | |||
| 841 | + | } | |||
| 842 | + | ||||
| 843 | + | .registration-prompt p { | |||
| 844 | + | margin: 0; | |||
| 845 | + | } | |||
| 846 | + | ||||
| 847 | + | @keyframes fadeIn { | |||
| 848 | + | from { | |||
| 849 | + | opacity: 0; | |||
| 850 | + | transform: translateY(-10px); | |||
| 851 | + | } | |||
| 852 | + | to { | |||
| 853 | + | opacity: 1; | |||
| 854 | + | transform: translateY(0); | |||
| 855 | + | } | |||
| 856 | + | } | |||
| 857 | + | ||||
| 858 | + | .registration-prompt .link-button { | |||
| 859 | + | color: #e65100; | |||
| 860 | + | text-decoration: underline; | |||
| 861 | + | cursor: pointer; | |||
| 862 | + | background: none; | |||
| 863 | + | border: none; | |||
| 864 | + | padding: 0; | |||
| 865 | + | font: inherit; | |||
| 866 | + | } | |||
| 867 | + | ||||
| 868 | + | .registration-prompt .link-button:hover { | |||
| 869 | + | color: #ef6c00; | |||
| 870 | + | text-decoration: none; | |||
| 871 | + | } | |||
| 809 | </style>⏎̸ | 872 | </style>⏎̸ | ||
M src/routes/+layout.svelte +1 -2
| @@ -226,8 +226,7 @@ | |||
| 226 | 226 | <title>{data.meta.title}</title> | |
| 227 | 227 | <meta name="description" content={data.meta.description} /> | |
| 228 | 228 | <meta name="keywords" content={data.meta.keywords} /> | |
| 229 | - | <meta name="language" content="French" /> | |
| 230 | - | <html lang="fr" /> | |
| 229 | + | <meta name="language" content="fr" /> | |
| 231 | 230 | ||
| 232 | 231 | <!-- Open Graph / Facebook --> | |
| 233 | 232 | <meta property="og:locale" content={data.meta.locale} /> | |
| @@ -226,8 +226,7 @@ | |||||
| 226 | <title>{data.meta.title}</title> | 226 | <title>{data.meta.title}</title> | ||
| 227 | <meta name="description" content={data.meta.description} /> | 227 | <meta name="description" content={data.meta.description} /> | ||
| 228 | <meta name="keywords" content={data.meta.keywords} /> | 228 | <meta name="keywords" content={data.meta.keywords} /> | ||
| 229 | - | <meta name="language" content="French" /> | 229 | + | <meta name="language" content="fr" /> |
| 230 | - | <html lang="fr" /> | |||
| 231 | 230 | ||||
| 232 | <!-- Open Graph / Facebook --> | 231 | <!-- Open Graph / Facebook --> | ||
| 233 | <meta property="og:locale" content={data.meta.locale} /> | 232 | <meta property="og:locale" content={data.meta.locale} /> | ||
M src/routes/api/chat/messages/+server.ts +2 -2
| @@ -75,9 +75,9 @@ export async function GET({ request, locals }) { | |||
| 75 | 75 | } | |
| 76 | 76 | ||
| 77 | 77 | // Fetch messages with a limit based on authentication status | |
| 78 | - | // Public requests are always limited to 10 messages | |
| 78 | + | // Public requests are limited to 50 messages | |
| 79 | 79 | // Authenticated users get 100 messages per request | |
| 80 | - | const fetchLimit = (isPublic || !locals.session) ? 10 : 100; | |
| 80 | + | const fetchLimit = (isPublic || !locals.session) ? 50 : 100; | |
| 81 | 81 | ||
| 82 | 82 | const fetchedMessages = await db.select() | |
| 83 | 83 | .from(messages) | |
| @@ -75,9 +75,9 @@ export async function GET({ request, locals }) { | |||||
| 75 | } | 75 | } | ||
| 76 | 76 | ||||
| 77 | // Fetch messages with a limit based on authentication status | 77 | // Fetch messages with a limit based on authentication status | ||
| 78 | - | // Public requests are always limited to 10 messages | 78 | + | // Public requests are limited to 50 messages |
| 79 | // Authenticated users get 100 messages per request | 79 | // Authenticated users get 100 messages per request | ||
| 80 | - | const fetchLimit = (isPublic || !locals.session) ? 10 : 100; | 80 | + | const fetchLimit = (isPublic || !locals.session) ? 50 : 100; |
| 81 | 81 | ||||
| 82 | const fetchedMessages = await db.select() | 82 | const fetchedMessages = await db.select() | ||
| 83 | .from(messages) | 83 | .from(messages) | ||