|
80 | 80 |
|
81 | 81 | try { |
82 | 82 | const devices = await navigator.mediaDevices.enumerateDevices(); |
83 | | - availableCameras = devices.filter(device => device.kind === 'videoinput'); |
| 83 | + availableCameras = devices.filter((device) => device.kind === 'videoinput'); |
84 | 84 |
|
85 | 85 | // Set default camera if none selected yet |
86 | 86 | if (availableCameras.length > 0 && !selectedCamera) { |
|
552 | 552 | }); |
553 | 553 |
|
554 | 554 | // Only update scores for remote users to prevent overriding local score |
555 | | - Object.keys(fetchedScores).forEach(userId => { |
| 555 | + Object.keys(fetchedScores).forEach((userId) => { |
556 | 556 | if (userId !== agoraClient?.uid?.toString()) { |
557 | 557 | scores[userId] = fetchedScores[userId]; |
558 | 558 | } |
|
618 | 618 | syncScore().then(() => { |
619 | 619 | // Determine current user's position |
620 | 620 | const currentUserId = agoraClient?.uid?.toString() || ''; |
621 | | - const userPosition = rankings.findIndex(r => r.userId === currentUserId) + 1; |
| 621 | + const userPosition = rankings.findIndex((r) => r.userId === currentUserId) + 1; |
622 | 622 | const userScore = scores[currentUserId] || 0; |
623 | | - |
| 623 | +
|
624 | 624 | // Create URL with search parameters |
625 | 625 | const searchParams = new URLSearchParams(); |
626 | 626 | searchParams.set('position', userPosition.toString()); |
627 | 627 | searchParams.set('score', userScore.toString()); |
628 | 628 | searchParams.set('total', rankings.length.toString()); |
629 | 629 | searchParams.set('roomId', roomId); |
630 | | - |
| 630 | +
|
631 | 631 | // Redirect to results page with parameters |
632 | 632 | toast.info('Competition ended!'); |
633 | 633 | setTimeout(() => { |
|
665 | 665 |
|
666 | 666 | // Request camera permissions early to make deviceId accessible |
667 | 667 | if (browser) { |
668 | | - navigator.mediaDevices.getUserMedia({ video: true }) |
| 668 | + navigator.mediaDevices |
| 669 | + .getUserMedia({ video: true }) |
669 | 670 | .then(() => getCameras()) |
670 | | - .catch(err => console.error('[CAMERA] Permission error:', err)); |
| 671 | + .catch((err) => console.error('[CAMERA] Permission error:', err)); |
671 | 672 | } |
672 | 673 |
|
673 | 674 | return () => { |
|
761 | 762 | </Card.Header> |
762 | 763 | <Card.Content class="space-y-4"> |
763 | 764 | <!-- Camera selector --> |
764 | | - {#if availableCameras.length > 1} |
765 | | - <div class="mb-2"> |
766 | | - <Select.Root |
767 | | - value={selectedCamera} |
768 | | - type="single" |
769 | | - onValueChange={(value) => switchCamera(value)} |
770 | | - > |
771 | | - <Select.Trigger class="w-full"> |
772 | | - {#if selectedCamera} |
773 | | - <span> |
774 | | - {availableCameras.find((camera) => camera.deviceId === selectedCamera) |
775 | | - ?.label || |
776 | | - `Camera ${availableCameras.indexOf(availableCameras.find((camera) => camera.deviceId === selectedCamera)!) + 1}`} |
777 | | - </span> |
778 | | - {:else} |
779 | | - <span>Select a camera</span> |
780 | | - {/if} |
781 | | - </Select.Trigger> |
782 | | - <Select.Content> |
783 | | - {#each availableCameras as camera} |
784 | | - <Select.Item value={camera.deviceId}> |
785 | | - {camera.label || `Camera ${availableCameras.indexOf(camera) + 1}`} |
786 | | - </Select.Item> |
787 | | - {/each} |
788 | | - </Select.Content> |
789 | | - </Select.Root> |
790 | | - </div> |
791 | | - {/if} |
| 765 | + <div class="mb-2"> |
| 766 | + <Select.Root |
| 767 | + value={selectedCamera} |
| 768 | + type="single" |
| 769 | + onValueChange={(value) => switchCamera(value)} |
| 770 | + > |
| 771 | + <Select.Trigger class="w-full"> |
| 772 | + {#if selectedCamera} |
| 773 | + <span> |
| 774 | + {availableCameras.find((camera) => camera.deviceId === selectedCamera) |
| 775 | + ?.label || |
| 776 | + `Camera ${availableCameras.indexOf(availableCameras.find((camera) => camera.deviceId === selectedCamera)!) + 1}`} |
| 777 | + </span> |
| 778 | + {:else} |
| 779 | + <span>Select a camera</span> |
| 780 | + {/if} |
| 781 | + </Select.Trigger> |
| 782 | + <Select.Content> |
| 783 | + {#each availableCameras as camera} |
| 784 | + <Select.Item value={camera.deviceId}> |
| 785 | + {camera.label || `Camera ${availableCameras.indexOf(camera) + 1}`} |
| 786 | + </Select.Item> |
| 787 | + {/each} |
| 788 | + </Select.Content> |
| 789 | + </Select.Root> |
| 790 | + </div> |
792 | 791 |
|
793 | 792 | <!-- Local stream (full width) --> |
794 | 793 | <div class="relative overflow-hidden rounded-lg bg-muted"> |
|
853 | 852 | <Card.Content> |
854 | 853 | <div class="space-y-4"> |
855 | 854 | <!-- Camera selector for waiting room --> |
856 | | - {#if availableCameras.length > 1} |
857 | | - <div class="mb-4"> |
858 | | - <label for="camera-select" class="mb-2 block text-sm font-medium">Select Camera</label |
859 | | - > |
860 | | - <Select.Root |
861 | | - type="single" |
862 | | - value={selectedCamera} |
863 | | - onValueChange={(value) => switchCamera(value)} |
864 | | - > |
865 | | - <Select.Trigger class="w-full"> |
866 | | - {#if selectedCamera} |
867 | | - <span> |
868 | | - {availableCameras.find((camera) => camera.deviceId === selectedCamera) |
869 | | - ?.label || |
870 | | - `Camera ${availableCameras.indexOf(availableCameras.find((camera) => camera.deviceId === selectedCamera)!) + 1}`} |
871 | | - </span> |
872 | | - {:else} |
873 | | - <span>Select a camera</span> |
874 | | - {/if} |
875 | | - </Select.Trigger> |
876 | | - <Select.Content> |
877 | | - {#each availableCameras as camera} |
878 | | - <Select.Item value={camera.deviceId}> |
879 | | - {camera.label || `Camera ${availableCameras.indexOf(camera) + 1}`} |
880 | | - </Select.Item> |
881 | | - {/each} |
882 | | - </Select.Content> |
883 | | - </Select.Root> |
884 | | - </div> |
885 | | - {/if} |
| 855 | + <div class="mb-4"> |
| 856 | + <label for="camera-select" class="mb-2 block text-sm font-medium">Select Camera</label> |
| 857 | + <Select.Root |
| 858 | + type="single" |
| 859 | + value={selectedCamera} |
| 860 | + onValueChange={(value) => switchCamera(value)} |
| 861 | + > |
| 862 | + <Select.Trigger class="w-full"> |
| 863 | + {#if selectedCamera} |
| 864 | + <span> |
| 865 | + {availableCameras.find((camera) => camera.deviceId === selectedCamera)?.label || |
| 866 | + `Camera ${availableCameras.indexOf(availableCameras.find((camera) => camera.deviceId === selectedCamera)!) + 1}`} |
| 867 | + </span> |
| 868 | + {:else} |
| 869 | + <span>Select a camera</span> |
| 870 | + {/if} |
| 871 | + </Select.Trigger> |
| 872 | + <Select.Content> |
| 873 | + {#each availableCameras as camera} |
| 874 | + <Select.Item value={camera.deviceId}> |
| 875 | + {camera.label || `Camera ${availableCameras.indexOf(camera) + 1}`} |
| 876 | + </Select.Item> |
| 877 | + {/each} |
| 878 | + </Select.Content> |
| 879 | + </Select.Root> |
| 880 | + </div> |
886 | 881 |
|
887 | 882 | <div> |
888 | 883 | <div class="mb-2 flex items-center gap-2"> |
|
0 commit comments