ok
This commit is contained in:
@@ -20,7 +20,7 @@ const Title = styled.h2`
|
||||
font-family: "Poppins", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-size: 6vw;
|
||||
font-size:${(props) => (props.HeaderSize)};
|
||||
color: rgba(88, 55, 50, 1);
|
||||
`;
|
||||
|
||||
@@ -214,6 +214,7 @@ const Child = styled.div`
|
||||
|
||||
const Header = ({
|
||||
HeaderText,
|
||||
HeaderSize='6vw',
|
||||
shopId,
|
||||
shopName,
|
||||
shopImage,
|
||||
@@ -300,7 +301,7 @@ const Header = ({
|
||||
};
|
||||
return (
|
||||
<HeaderBar>
|
||||
<Title>
|
||||
<Title HeaderSize={HeaderSize}>
|
||||
{shopName == null
|
||||
? HeaderText == null
|
||||
? "kedaimaster"
|
||||
@@ -309,9 +310,9 @@ const Header = ({
|
||||
</Title>
|
||||
<div style={{ visibility: showProfile ? "visible" : "hidden" }}>
|
||||
<ProfileImage
|
||||
src={shopImage || "https://static-00.iconduck.com/assets.00/profile-major-icon-1024x1024-9rtgyx30.png"}
|
||||
src={user.username == undefined? shopImage : "https://static-00.iconduck.com/assets.00/profile-major-icon-1024x1024-9rtgyx30.png"}
|
||||
alt="Profile"
|
||||
onClick={handleImageClick}
|
||||
onClick={user.username !== undefined?handleImageClick: null}
|
||||
animate={showRectangle && animate}
|
||||
/>
|
||||
<ProfileName animate={showRectangle && animate}>
|
||||
@@ -325,9 +326,6 @@ const Header = ({
|
||||
this is the guest side of {guestSideOfClerk.clerkUsername}
|
||||
</Child>
|
||||
)}
|
||||
{user.username === undefined && !guestSideOfClerk && (
|
||||
<Child onClick={goToLogin}>Click to login</Child>
|
||||
)}
|
||||
{user.username !== undefined && (
|
||||
<Child onClick={() => setModal("edit_account")}>
|
||||
Ubah profil
|
||||
@@ -340,22 +338,17 @@ const Header = ({
|
||||
user.username !== undefined &&
|
||||
user.roleId === 1 && (
|
||||
<>
|
||||
|
||||
{/* <Child onClick={() => setModal("update_stock")}>
|
||||
update stock
|
||||
</Child> */}
|
||||
<Child hasChildren>
|
||||
{shopName}
|
||||
|
||||
<div class="toggle-switch">
|
||||
<label class="toggle-switch-label" for="toggleSwitch">
|
||||
Mode edit
|
||||
</label>
|
||||
<Child>
|
||||
Mode edit
|
||||
<Switch
|
||||
borderRadius={0}
|
||||
checked={isEditMode}
|
||||
onChange={() => setIsEditMode(!isEditMode)}
|
||||
/>
|
||||
</div>
|
||||
</Child>
|
||||
|
||||
<Child onClick={() => setModal("welcome_config")}>
|
||||
Halaman sambutan
|
||||
@@ -401,15 +394,14 @@ const Header = ({
|
||||
<Child hasChildren>
|
||||
{shopName}
|
||||
|
||||
<div class="toggle-switch">
|
||||
<label class="toggle-switch-label" for="toggleSwitch">
|
||||
Mode edit
|
||||
</label>
|
||||
<Switch
|
||||
checked={isEditMode}
|
||||
onChange={() => setIsEditMode(!isEditMode)}
|
||||
/>
|
||||
</div>
|
||||
<Child>
|
||||
Mode edit
|
||||
<Switch
|
||||
borderRadius={0}
|
||||
checked={isEditMode}
|
||||
onChange={() => setIsEditMode(!isEditMode)}
|
||||
/>
|
||||
</Child>
|
||||
<Child onClick={() => setModal("add_material")}>
|
||||
stok
|
||||
</Child>
|
||||
|
||||
@@ -18,6 +18,9 @@ import NotificationRequest from "../pages/NotificationRequest.js";
|
||||
import NotificationBlocked from "../pages/NotificationBlocked.js";
|
||||
import WelcomePageEditor from "../pages/WelcomePageEditor.js";
|
||||
import GuidePage from "../pages/GuidePage";
|
||||
import Join from "../pages/Join";
|
||||
import Login from "../pages/Login";
|
||||
import ResetPassword from "../pages/ResetPassword";
|
||||
const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
@@ -35,6 +38,8 @@ const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
|
||||
return (
|
||||
<div onClick={handleOverlayClick} className={styles.modalOverlay}>
|
||||
<div className={styles.modalContent} onClick={handleContentClick}>
|
||||
{modalContent === "join" && <Join />}
|
||||
{modalContent === "reset-password" && <ResetPassword />}
|
||||
{modalContent === "req_notification" && <NotificationRequest setModal={setModal} />}
|
||||
{modalContent === "blocked_notification" && <NotificationBlocked />}
|
||||
{modalContent === "create_clerk" && <CreateClerk shopId={shop.cafeId} />}
|
||||
|
||||
@@ -134,7 +134,7 @@ const MusicComponent = ({ song, min, max, onDecision }) => {
|
||||
<p className="artist-name">{song.artist}</p>
|
||||
{min < 0 && <p className="artist-name"><--- {song.disagree} no - {song.agree} yes ---></p>}
|
||||
</div>
|
||||
<p className="song-duration">{formatDuration(song.duration_ms)}</p>
|
||||
<p className="song-duration">{song.length}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import API_BASE_URL from "../config.js";
|
||||
import "./MusicPlayer.css";
|
||||
import MusicComponent from "./MusicComponent";
|
||||
|
||||
export function MusicPlayer({ socket, shopId, user, isSpotifyNeedLogin }) {
|
||||
export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLogin, queue }) {
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [trackLength, setTrackLength] = useState(0);
|
||||
const [viewing, setViewing] = useState(false); // State for expansion
|
||||
@@ -13,7 +13,6 @@ export function MusicPlayer({ socket, shopId, user, isSpotifyNeedLogin }) {
|
||||
const [debouncedSongName, setDebouncedSongName] = useState(songName);
|
||||
const [currentSong, setCurrentSong] = useState([]);
|
||||
const [songs, setSongs] = useState([]);
|
||||
const [queue, setQueue] = useState([]);
|
||||
const [paused, setPaused] = useState([]);
|
||||
|
||||
const [lyrics, setLyrics] = useState([]);
|
||||
@@ -111,6 +110,11 @@ export function MusicPlayer({ socket, shopId, user, isSpotifyNeedLogin }) {
|
||||
fetchColor();
|
||||
}, [currentSong]);
|
||||
|
||||
const convertToMilliseconds = (timeStr) => {
|
||||
const [minutes, seconds] = timeStr.split(':').map(Number);
|
||||
return (minutes * 60 + seconds) * 1000;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
|
||||
@@ -123,13 +127,9 @@ export function MusicPlayer({ socket, shopId, user, isSpotifyNeedLogin }) {
|
||||
setCurrentSong(response);
|
||||
setCurrentTime(response.progress_ms / 1000); // Convert milliseconds to seconds
|
||||
setLyricProgressMs(response.progress_ms);
|
||||
setTrackLength(response.item.duration_ms / 1000);
|
||||
setTrackLength(convertToMilliseconds(response.item.length));
|
||||
});
|
||||
|
||||
socket.on("updateQueue", (response) => {
|
||||
setQueue(response);
|
||||
console.log(response);
|
||||
});
|
||||
|
||||
socket.on("updatePlayer", (response) => {
|
||||
setPaused(response.decision);
|
||||
@@ -149,42 +149,61 @@ export function MusicPlayer({ socket, shopId, user, isSpotifyNeedLogin }) {
|
||||
socket.off("searchResponse");
|
||||
};
|
||||
}, [socket]);
|
||||
|
||||
// useEffect for setting up the socket listener
|
||||
useEffect(() => {
|
||||
const handleUpdateCanvas = (response) => {
|
||||
if (response && response !== canvaz) {
|
||||
console.log(response);
|
||||
console.log(canvaz);
|
||||
setCanvaz(response);
|
||||
fetch(response)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
setVideoSrc(blobUrl);
|
||||
|
||||
if (videoRef.current) {
|
||||
videoRef.current.load(); // Reload the video element
|
||||
}
|
||||
})
|
||||
.catch((error) => console.error('Error loading video:', error));
|
||||
} else if (!response) {
|
||||
// Clear the video source if response is empty
|
||||
setVideoSrc('');
|
||||
if (videoRef.current) {
|
||||
videoRef.current.load(); // Reload the video element
|
||||
// useEffect for setting up the socket listener
|
||||
useEffect(() => {
|
||||
const handleUpdateCanvas = (response) => {
|
||||
if (response && response !== canvaz) {
|
||||
console.log(response);
|
||||
console.log(canvaz);
|
||||
setCanvaz(response);
|
||||
fetch(response)
|
||||
.then((response) => response.blob())
|
||||
.then((blob) => {
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
setVideoSrc(blobUrl);
|
||||
|
||||
if (videoRef.current) {
|
||||
videoRef.current.load(); // Reload the video element
|
||||
}
|
||||
})
|
||||
.catch((error) => console.error('Error loading video:', error));
|
||||
} else if (!response) {
|
||||
// Clear the video source if response is empty
|
||||
setVideoSrc('');
|
||||
if (videoRef.current) {
|
||||
videoRef.current.load(); // Reload the video element
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Listen for the "updateCanvas" event
|
||||
socket.on("updateCanvas", handleUpdateCanvas);
|
||||
// Listen for the "updateCanvas" event
|
||||
socket.on("updateCanvas", handleUpdateCanvas);
|
||||
|
||||
// Clean up the socket listener when the component is unmounted
|
||||
return () => {
|
||||
socket.off("updateCanvas", handleUpdateCanvas);
|
||||
};
|
||||
}, [socket, canvaz]);
|
||||
socket.on("claimPlayerRes", (response) => {
|
||||
if (response.error) {
|
||||
console.log('Error:', response.error);
|
||||
// Handle error
|
||||
} else {
|
||||
window.open(response.url);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("unClaimPlayerRes", (response) => {
|
||||
if (response.error) {
|
||||
console.log('Error:', response.error);
|
||||
// Handle error
|
||||
} else {
|
||||
console.log('Player token:', response.token);
|
||||
// Handle success and use the player token
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up the socket listener when the component is unmounted
|
||||
return () => {
|
||||
socket.off("updateCanvas", handleUpdateCanvas);
|
||||
};
|
||||
}, [socket, canvaz]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate progress every 100ms
|
||||
@@ -235,10 +254,10 @@ useEffect(() => {
|
||||
setSongName(event.target.value);
|
||||
};
|
||||
|
||||
const onRequest = (trackId) => {
|
||||
const onRequest = (track) => {
|
||||
const token = localStorage.getItem("auth");
|
||||
if (socket != null && token) {
|
||||
socket.emit("songRequest", { token, shopId, trackId });
|
||||
socket.emit("songRequest", { token, shopId, track });
|
||||
setSongName("");
|
||||
}
|
||||
};
|
||||
@@ -262,19 +281,20 @@ useEffect(() => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSpotifyAuth = () => {
|
||||
const handleSetPlayer = () => {
|
||||
const token = localStorage.getItem("auth");
|
||||
let nextUrl = ""; // Use 'let' since the value will change
|
||||
if (isSpotifyNeedLogin) {
|
||||
nextUrl = API_BASE_URL + `/login?token=${token}&cafeId=${shopId}`;
|
||||
} else {
|
||||
nextUrl = API_BASE_URL + `/logout?token=${token}&cafeId=${shopId}`;
|
||||
}
|
||||
window.location.href = nextUrl;
|
||||
};
|
||||
|
||||
const handleLogin = () => {
|
||||
// navigate(`/login/${shopId}`);
|
||||
if (isSpotifyNeedLogin) {
|
||||
socket.emit("claimPlayer", {
|
||||
token,
|
||||
shopId,
|
||||
});
|
||||
} else {
|
||||
socket.emit("unClaimPlayer", {
|
||||
token,
|
||||
shopId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -354,7 +374,7 @@ useEffect(() => {
|
||||
<div
|
||||
onClick={toggleView}
|
||||
className="current-bgr"
|
||||
style={{ backgroundImage: `url(${videoSrc != ""? '':backgroundImage})` }}
|
||||
style={{ backgroundImage: `url(${videoSrc != "" ? '' : backgroundImage})` }}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
@@ -362,7 +382,7 @@ useEffect(() => {
|
||||
loop
|
||||
playsInline
|
||||
muted
|
||||
style={{ height: '100%', width: '100%', objectFit: 'cover', position:'absolute', top:0,right:0,zIndex:-1}}
|
||||
style={{ height: '100%', width: '100%', objectFit: 'cover', position: 'absolute', top: 0, right: 0, zIndex: -1 }}
|
||||
>
|
||||
{videoSrc && <source src={videoSrc} type="video/mp4" />}
|
||||
</video>
|
||||
@@ -432,14 +452,14 @@ useEffect(() => {
|
||||
className={`expandable-container ${expanded ? "expanded" : ""}`}
|
||||
ref={expandableContainerRef}
|
||||
>
|
||||
{user.cafeId != null && user.cafeId == shopId && (
|
||||
{user.cafeId == shopId || user.userId == shopOwnerId && (
|
||||
<div className="auth-box">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={
|
||||
isSpotifyNeedLogin ? "Login Spotify" : "Logout Spotify"
|
||||
isSpotifyNeedLogin ? "Set as music player" : "Unset as music player"
|
||||
}
|
||||
onClick={handleSpotifyAuth}
|
||||
onClick={handleSetPlayer}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -466,20 +486,25 @@ useEffect(() => {
|
||||
song={song}
|
||||
min={0}
|
||||
max={100}
|
||||
onDecision={(e) => onRequest(song.trackId)}
|
||||
/>
|
||||
))}
|
||||
{songName == "" &&
|
||||
queue.length > 0 &&
|
||||
queue.map((song, index) => (
|
||||
<MusicComponent
|
||||
key={index}
|
||||
song={song}
|
||||
min={-100}
|
||||
max={100}
|
||||
onDecision={(vote) => onDecision(song.trackId, vote)}
|
||||
onDecision={(e) => onRequest(song)}
|
||||
/>
|
||||
))}
|
||||
{
|
||||
songName === "" &&
|
||||
queue &&
|
||||
Array.isArray(queue) &&
|
||||
queue.length > 0 && (
|
||||
queue.map((song, index) => (
|
||||
<MusicComponent
|
||||
key={index}
|
||||
song={song}
|
||||
min={-100}
|
||||
max={100}
|
||||
onDecision={(vote) => onDecision(song.trackId, vote)}
|
||||
/>
|
||||
))
|
||||
)
|
||||
}
|
||||
{songName == "" && queue.length < 1 && (
|
||||
<div className="rectangle">
|
||||
<div className="diagonal-text">No Beats Ahead - Drop Your Hits</div>
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/* src/RouletteWheel.css */
|
||||
/* html,
|
||||
body,
|
||||
img {
|
||||
overflow: hidden;
|
||||
} */
|
||||
|
||||
.roulette-wheel-container {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
/* Center the container horizontally */
|
||||
top: 30%;
|
||||
transform: translate(-40%, 20%);
|
||||
scale: 3;
|
||||
max-width: 100vw;
|
||||
/* Limit container width to viewport width */
|
||||
max-height: 100vh;
|
||||
/* Limit container height to viewport height */
|
||||
overflow: hidden;
|
||||
/* Hide overflowing content */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
/* Safari */
|
||||
-ms-user-select: none;
|
||||
/* IE 10 and IE 11 */
|
||||
user-select: none;
|
||||
/* Standard syntax */
|
||||
}
|
||||
|
||||
.roulette-wheel {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: auto;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
.roulette-image {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
user-select: none;
|
||||
/* Prevents the image from being selected */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.roulette-input {
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
/* Increase size for better visibility */
|
||||
height: auto;
|
||||
/* Increase size for better visibility */
|
||||
border: none;
|
||||
/* Remove border for simplicity */
|
||||
border-radius: 5px;
|
||||
/* Add border radius for rounded corners */
|
||||
color: rgb(2, 2, 2);
|
||||
/* Text color */
|
||||
font-size: 16px;
|
||||
/* Font size */
|
||||
border: 2px solid #ccc;
|
||||
}
|
||||
|
||||
.roulette-button {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
width: 30%;
|
||||
height: auto;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
color: rgb(2, 2, 2);
|
||||
font-size: 16px;
|
||||
border: 2px solid #ccc;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.roulette-button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.roulette-button.error {
|
||||
background-color: red; /* Change button color on error */
|
||||
transition: background-color 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import "./RouletteWheel.css";
|
||||
import coffeeImage from "./coffee.png"; // Update the path to your image
|
||||
|
||||
import { ThreeDots } from "react-loader-spinner";
|
||||
|
||||
const RouletteWheel = ({ isForRegister, onSignIn, onSignUp }) => {
|
||||
const [rotation, setRotation] = useState(0);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const startAngleRef = useRef(0);
|
||||
const startRotationRef = useRef(0);
|
||||
const wheelRef = useRef(null);
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const emailInputRef = useRef(null);
|
||||
const usernameInputRef = useRef(null);
|
||||
const passwordInputRef = useRef(null);
|
||||
|
||||
const handleSignIn = () => {
|
||||
setError(false); // Reset error state before login attempt
|
||||
onSignIn(email, username, password, setLoading, setError);
|
||||
};
|
||||
|
||||
const handleSignUp = () => {
|
||||
setError(false); // Reset error state before login attempt
|
||||
onSignUp(email, username, password, setLoading, setError);
|
||||
};
|
||||
const handleStart = (x, y) => {
|
||||
setIsDragging(true);
|
||||
startAngleRef.current = getAngle(x, y);
|
||||
startRotationRef.current = rotation;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setError(false);
|
||||
}, [error]);
|
||||
|
||||
const handleMove = (x, y) => {
|
||||
if (isDragging) {
|
||||
const angle = getAngle(x, y);
|
||||
const deltaAngle = angle - startAngleRef.current;
|
||||
setRotation(startRotationRef.current + deltaAngle);
|
||||
if (isForRegister) {
|
||||
if (rotation + deltaAngle > 30 || rotation + deltaAngle < -210)
|
||||
handleEnd();
|
||||
} else {
|
||||
if (rotation + deltaAngle > 30 || rotation + deltaAngle < -120)
|
||||
handleEnd();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleEnd = () => {
|
||||
setIsDragging(false);
|
||||
setRotation((prevRotation) => {
|
||||
const snappedRotation = Math.round(prevRotation / 90) * 90;
|
||||
return snappedRotation;
|
||||
});
|
||||
};
|
||||
|
||||
const handleMouseDown = (e) => {
|
||||
handleStart(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
handleMove(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
handleEnd();
|
||||
};
|
||||
|
||||
const handleTouchStart = (e) => {
|
||||
const touch = e.touches[0];
|
||||
handleStart(touch.clientX, touch.clientY);
|
||||
};
|
||||
|
||||
const handleTouchMove = (e) => {
|
||||
if (!isDragging) return;
|
||||
e.preventDefault();
|
||||
const touch = e.touches[0];
|
||||
handleMove(touch.clientX, touch.clientY);
|
||||
};
|
||||
|
||||
const handleTouchEnd = (e) => {
|
||||
handleEnd();
|
||||
};
|
||||
|
||||
const handleChildMouseDown = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const handleChildTouchStart = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
const getAngle = (x, y) => {
|
||||
const rect = wheelRef.current.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const dx = x - centerX;
|
||||
const dy = y - centerY;
|
||||
return Math.atan2(dy, dx) * (180 / Math.PI);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
document.addEventListener("touchmove", handleTouchMove, {
|
||||
passive: false,
|
||||
});
|
||||
document.addEventListener("touchend", handleTouchEnd, { passive: false });
|
||||
} else {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
document.removeEventListener("touchmove", handleTouchMove);
|
||||
document.removeEventListener("touchend", handleTouchEnd);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
document.removeEventListener("touchmove", handleTouchMove);
|
||||
document.removeEventListener("touchend", handleTouchEnd);
|
||||
};
|
||||
}, [isDragging]);
|
||||
|
||||
const inputPositions = [-90, 0, 90, 180]; // Positions for the inputs
|
||||
|
||||
const isVisible = (angle) => {
|
||||
const modAngle = ((angle % 360) + 360) % 360;
|
||||
return modAngle % 90 === 0;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isForRegister) {
|
||||
if (isVisible(rotation % 360 !== -0)) {
|
||||
emailInputRef.current.focus();
|
||||
} else if (isVisible(rotation % 360 !== -90)) {
|
||||
usernameInputRef.current.focus();
|
||||
} else if (isVisible(rotation % 360 !== -180)) {
|
||||
passwordInputRef.current.focus();
|
||||
}
|
||||
} else {
|
||||
if (isVisible(rotation % 360 !== -0)) {
|
||||
usernameInputRef.current.focus();
|
||||
} else if (isVisible(rotation % 360 !== -90)) {
|
||||
passwordInputRef.current.focus();
|
||||
}
|
||||
}
|
||||
}, [rotation]);
|
||||
|
||||
return (
|
||||
<div className="roulette-wheel-container">
|
||||
<div
|
||||
className="roulette-wheel"
|
||||
ref={wheelRef}
|
||||
onMouseDown={handleMouseDown}
|
||||
onTouchStart={handleTouchStart}
|
||||
style={{ transform: `rotate(${rotation}deg)` }}
|
||||
>
|
||||
<input
|
||||
className={`roulette-input ${
|
||||
isVisible(rotation % 360 !== -0) ? "" : "hidden"
|
||||
}`}
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
ref={usernameInputRef}
|
||||
style={{ transform: "translate(60%, -220%) rotate(0deg)" }}
|
||||
/>
|
||||
<input
|
||||
className={`roulette-input ${
|
||||
isVisible(rotation % 360 !== -0) ? "" : "hidden"
|
||||
}`}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
ref={passwordInputRef}
|
||||
style={{ transform: "translate(90%, -90%) rotate(0deg)" }}
|
||||
/>
|
||||
<button
|
||||
className={`roulette-button ${error ? "error" : ""} ${
|
||||
isVisible(rotation % 360 !== -0) ? "" : "hidden"
|
||||
}`}
|
||||
disabled={loading}
|
||||
onClick={handleSignIn}
|
||||
onMouseDown={handleChildMouseDown}
|
||||
onTouchStart={handleChildTouchStart}
|
||||
style={{ transform: "translate(110%, -10%) rotate(0deg)" }}
|
||||
>
|
||||
{loading ? (
|
||||
<ThreeDots color="black" height={15} width={40} /> // Show loader when loading
|
||||
) : (
|
||||
"Sign in"
|
||||
)}
|
||||
</button>
|
||||
<input
|
||||
className={`roulette-input ${
|
||||
isVisible(rotation % 360 !== -90) ? "" : "hidden"
|
||||
}`}
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
ref={emailInputRef}
|
||||
style={{ transform: "translate(30%, 320%) rotate(90deg)" }}
|
||||
/>
|
||||
<input
|
||||
className={`roulette-input ${
|
||||
isVisible(rotation % 360 !== -90) ? "" : "hidden"
|
||||
}`}
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
ref={usernameInputRef}
|
||||
style={{ transform: "translate(10%, 320%) rotate(90deg)" }}
|
||||
/>
|
||||
<input
|
||||
className={`roulette-input ${
|
||||
isVisible(rotation % 360 !== -90) ? "" : "hidden"
|
||||
}`}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
ref={passwordInputRef}
|
||||
style={{ transform: "translate(-10%, 320%) rotate(90deg)" }}
|
||||
/>
|
||||
<button
|
||||
className={`roulette-button ${error ? "error" : ""} ${
|
||||
isVisible(rotation % 360 !== -90) ? "" : "hidden"
|
||||
} `}
|
||||
onClick={handleSignUp}
|
||||
onMouseDown={handleChildMouseDown}
|
||||
onTouchStart={handleChildTouchStart}
|
||||
style={{ transform: "translate(-60%, 320%) rotate(90deg)" }}
|
||||
>
|
||||
{loading ? (
|
||||
<ThreeDots color="black" height={15} width={40} /> // Show loader when loading
|
||||
) : (
|
||||
"Sign Up"
|
||||
)}
|
||||
</button>
|
||||
<img src={coffeeImage} className="roulette-image" alt="Coffee" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RouletteWheel;
|
||||
@@ -1,108 +1,39 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import YouTube from 'react-youtube';
|
||||
|
||||
export function TrackPlayer({ next }) {
|
||||
// State to store the progress in milliseconds, video duration, and the next video ID
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [currentTrack, setCurrentTrack] = useState(null); // Initial video ID
|
||||
const [nextTrack, setNextTrack] = useState(null); // Initial video ID
|
||||
const [isNearEnd, setIsNearEnd] = useState(false); // Flag for 20 seconds left
|
||||
const playerRef = useRef(null);
|
||||
export function TrackPlayer({ next, queueList, setQueueList }) {
|
||||
// Initial list of video IDs (YouTube video IDs)
|
||||
const videos = [
|
||||
'dQw4w9WgXcQ', // Example Video 1
|
||||
'tgbNymZ7vqY', // Example Video 2
|
||||
'oHg5SJYRHA0', // Example Video 3
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (next == null) return;
|
||||
if (currentTrack == null) setCurrentTrack(next);
|
||||
setNextTrack(next);
|
||||
}, [next]);
|
||||
// State to track the current video index
|
||||
const [currentVideoIndex, setCurrentVideoIndex] = useState(0);
|
||||
|
||||
const handlePlayerStateChange = (event) => {
|
||||
if (event.data === window.YT.PlayerState.PLAYING) {
|
||||
|
||||
// Start tracking progress once the video starts playing
|
||||
const interval = setInterval(() => {
|
||||
if (playerRef.current) {
|
||||
const currentTime = playerRef.current.getCurrentTime(); // Get current time in seconds
|
||||
setProgress(currentTime * 1000); // Convert to milliseconds
|
||||
// Check if the video is 20 seconds from ending
|
||||
if (currentTime >= duration / 1000 - 20 && !isNearEnd) {
|
||||
setIsNearEnd(true);
|
||||
} else if (currentTime < duration / 1000 - 20 && isNearEnd) {
|
||||
setIsNearEnd(false);
|
||||
}
|
||||
}
|
||||
}, 100); // Update every 100 ms
|
||||
|
||||
// Clean up when the video is paused or finished
|
||||
event.target.addEventListener('onStateChange', (e) => {
|
||||
if (e.data === window.YT.PlayerState.PAUSED || e.data === window.YT.PlayerState.ENDED) {
|
||||
clearInterval(interval);
|
||||
|
||||
// When the video ends, set the next track
|
||||
if (e.data === window.YT.PlayerState.ENDED) {
|
||||
// Logic to set the next video ID (for now, just updating to another static video)
|
||||
setCurrentTrack(nextTrack); // Replace 'newVideoId' with the ID of the next video you want
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Function to handle video end event
|
||||
const handleVideoEnd = () => {
|
||||
// Update to next video or loop to the first video if at the end
|
||||
setCurrentVideoIndex((prevIndex) => (prevIndex + 1) % videos.length);
|
||||
};
|
||||
|
||||
const handlePlayerReady = (event) => {
|
||||
playerRef.current = event.target;
|
||||
const durationInSeconds = playerRef.current.getDuration(); // Get video duration in seconds
|
||||
setDuration(durationInSeconds * 1000); // Set the duration in milliseconds
|
||||
|
||||
// Set the video quality to the lowest available (typically 360p or smaller)
|
||||
playerRef.current.setPlaybackQuality('small');
|
||||
// YouTube video player options
|
||||
const opts = {
|
||||
height: '390',
|
||||
width: '640',
|
||||
playerVars: {
|
||||
autoplay: 1, // Automatically play the video
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Make sure to load the YouTube iframe API script when the component mounts
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://www.youtube.com/iframe_api';
|
||||
document.body.appendChild(script);
|
||||
|
||||
return () => {
|
||||
// Cleanup if needed (for example, removing the script)
|
||||
document.body.removeChild(script);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// When the currentTrack changes, reset progress and duration
|
||||
setProgress(0);
|
||||
setDuration(0);
|
||||
setIsNearEnd(false);
|
||||
}, [currentTrack]);
|
||||
|
||||
return (
|
||||
<div className="App" style={{visibility: 'hidden', position: 'fixed'}}>
|
||||
{currentTrack != null && (
|
||||
<div>
|
||||
<YouTube
|
||||
videoId={currentTrack.videoId} // Dynamically change video based on currentTrack
|
||||
opts={{
|
||||
height: '315',
|
||||
width: '560',
|
||||
playerVars: {
|
||||
autoplay: 1,
|
||||
controls: 1,
|
||||
mute: 0,
|
||||
loop: 0, // Do not loop; handle next video manually
|
||||
quality: 'small', // Request small quality (360p or lower)
|
||||
},
|
||||
}}
|
||||
onReady={handlePlayerReady} // Get duration and set quality on ready
|
||||
onStateChange={handlePlayerStateChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>Progress: {progress} ms</div>
|
||||
<div>Video Duration: {duration} ms</div>
|
||||
<div>
|
||||
{isNearEnd && <div>Video is near the end (20 seconds left)</div>}
|
||||
</div>
|
||||
<div>
|
||||
<YouTube
|
||||
videoId={videos[currentVideoIndex]} // Use the current video ID
|
||||
opts={opts}
|
||||
onEnd={handleVideoEnd} // Call handleVideoEnd when the video ends
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user