Вот полная структура файла проекта, все организовано и правильно расположено, никаких проблем. Ниже я выложу код, который у меня есть на данный момент, и отвечу на возникшие у меня вопросы и проблемы.
Вот гифка приложения (трудно увидеть), пользовательский интерфейс в основном в порядке, я планирую настроить его позже. Музыкальные карты предназначены для воспроизведения песен, которые я сохранил в папке аудио, при нажатии пользователем.
Раньше мне удавалось отображать страницы входа и регистрации, и когда пользователь входил в систему, кнопка входа менялась на выход из системы и возвращала пользователя обратно на страницу входа; однако эта функция перестала работать. Как мне снова заставить обе страницы работать?
Когда я перехожу на каждую соответствующую страницу, вместо этого я просто попадаю на панель управления. Я бы хотел, чтобы панель управления была защищена, чтобы к ней могли получить доступ только вошедшие в систему пользователи, в противном случае им придется либо создать новую учетную запись, либо войти в систему с существующей учетной записью (с использованием аутентификации Firebase).
Кроме того, как мне заставить каждую отдельную песню воспроизводиться при нажатии соответствующей карты, у меня все мои аудиофайлы сохранены в папке audio в папке ресурсов, и мне снова удалось заставить ее работать раньше, но я случайно сломал код, используя Chatgpt для ответов (урок усвоен с трудом) способ).
Index.html (лишние карточки опущены из-за ограничения на количество символов)
Код: Выделить всё
AudioKi
[i][/i]
[i][/i]
[i][/i]
[i] type="text"
class="input-box"
placeholder="What do you wanna listen to"
/>
[/i]
Premium
Support
Download
[i][/i] Install App
Sign up
Log in
Your Library
+
Playlists
Podcasts
Artists
Events
[h4]Create your first playlist[/h4]
It's easy — we'll get you started
Create Playlist
[h4]Let's find some podcasts[/h4]
We'll keep you updated on the latest
Browse Podcasts
Experiments, Vol 1
[i]
[/i]
ambient (aura)
rokiiyotakka
Experiments, Vol 2
[i]
[/i]
amber glow
rokiiyotakka
More by rokiiyotakka
[i]
[/i]
New Jazz Who Dis?
rokiiyotakka
[i]
[/i]
Sunset Boulevard
rokiiyotakka
[i]
[/i]
Street Cred
rokiiyotakka
[i]
[/i]
Back of Da Clerb
rokiiyotakka
[i]
[/i]
Kii's Beat
rokiiyotakka
[i]
[/i]
Dancehall Attempt
rokiiyotakka
[i]
[/i]
Bomboclaat
rokiiyotakka
[i]
[/i]
Kii's Beat Pt 2
rokiiyotakka
[i]
[/i]
In Da Clerb We All Fam
rokiiyotakka
[i]
[/i]
D-D-D-Drop It Again
rokiiyotakka
[i]
[/i]
Hit My Line
rokiiyotakka
[i]
[/i]
Reservation At Eight
rokiiyotakka
[i]
[/i]
Island Tune
rokiiyotakka
[i]
[/i]
Welcom to the Eighties
rokiiyotakka
[i]
[/i]
Trappin'
rokiiyotakka
[i]
New Jazz Who Dis?
rokiiyotakka
[/i]
[i][/i]
[i][/i]
[i][/i]
[i][/i]
Код: Выделить всё
(omitted due to character limit)
Код: Выделить всё
// src/components/bottomplayer.jsx
import React, { useRef, useState, useEffect } from "react";
export default function BottomPlayer({ song }) {
const audioRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
useEffect(() => {
if (song && audioRef.current) {
audioRef.current.src = song.audio;
audioRef.current.play().then(() => setIsPlaying(true)).catch(() => setIsPlaying(false));
setProgress(0);
}
}, [song]);
const handleTimeUpdate = () => {
if (!audioRef.current) return;
setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100);
};
const handleSeek = (e) => {
if (!audioRef.current) return;
audioRef.current.currentTime = (e.target.value / 100) * audioRef.current.duration;
setProgress(e.target.value);
};
const togglePlay = () => {
if (!audioRef.current) return;
if (audioRef.current.paused) {
audioRef.current.play();
setIsPlaying(true);
} else {
audioRef.current.pause();
setIsPlaying(false);
}
};
if (!song) return null;
return (
[img]{song.img} alt={song.title} /[/img]
{song.title}
{song.desc}
{isPlaying ? "⏸" : "▶"}
setIsPlaying(false)}
/>
);
}
Код: Выделить всё
// src/components/LeftSidebar.jsx
import React from "react";
export default function LeftSidebar() {
return (
Your Library
+
Playlists
Podcasts
Artists
Events
[h4]Create your first playlist[/h4]
It's easy — we'll get you started
Create Playlist
[h4]Let's find some podcasts[/h4]
We'll keep you updated on the latest
Browse Podcasts
);
}
Код: Выделить всё
import React from "react";
export default function MusicSection({ title, songs, playSong }) {
return (
{title}
{songs.map((song, index) => (
[img]{song.img} alt={song.title} /[/img]
playSong(song)}>
▶
{song.title}
{song.desc}
))}
);
}
Код: Выделить всё
// src/components/navbar.jsx
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { auth } from "../firebase";
export default function Navbar() {
const [user, setUser] = useState(null);
const navigate = useNavigate();
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((currentUser) => {
setUser(currentUser);
});
return () => unsubscribe();
}, []);
const handleLogout = async () => {
await auth.signOut();
navigate("/login");
};
return (
SPOTIFY
{user ? (
Logout
) : (
Login
Sign Up
)}
);
}
Код: Выделить всё
// src/pages/dashboard.jsx
import React, { useState, useRef, useEffect } from "react";
import Navbar from "../components/navbar";
import LeftSidebar from "../components/leftsidebar";
import MusicSection from "../components/musicsection";
import BottomPlayer from "../components/bottomplayer";
// 🔥 DEFINE SONG ARRAYS
const experimentsVol1 = [
{ title: "ambient (aura)", desc: "rokiiyotakka", img: "1.png", audio: audio1 },
{ title: "80's nostalgia", desc: "rokiiyotakka", img: "1.png", audio: audio2 },
{ title: "close encounter", desc: "rokiiyotakka", img: "1.png", audio: audio3 },
{ title: "corvette²", desc: "rokiiyotakka", img: "1.png", audio: audio4 },
{ title: "grungecore", desc: "rokiiyotakka", img: "1.png", audio: audio5 },
{ title: "illusions of love", desc: "rokiiyotakka", img: "1.png", audio: audio6 },
{ title: "let the bass carry you", desc: "rokiiyotakka", img: "1.png", audio: audio7 },
{ title: "magenta skies", desc: "rokiiyotakka", img: "1.png", audio: audio8 },
{ title: "melting heart", desc: "rokiiyotakka", img: "1.png", audio: audio9 },
{ title: "space jam", desc: "rokiiyotakka", img: "1.png", audio: audio10 },
{ title: "time keeps ticking", desc: "rokiiyotakka", img: "1.png", audio: audio11 },
{ title: "warm as a hug", desc: "rokiiyotakka", img: "1.png", audio: audio12 },
{ title: "cherry bomb", desc: "rokiiyotakka", img: "1.png", audio: audio13 },
];
const experimentsVol2 = [
{ title: "amber glow", desc: "rokiiyotakka", img: "2.png", audio: audio14 },
{ title: "dixie road", desc: "rokiiyotakka", img: "2.png", audio: audio15 },
{ title: "early 2000's type beat", desc: "rokiiyotakka", img: "2.png", audio: audio16 },
{ title: "ecstasy is pure bliss", desc: "rokiiyotakka", img: "2.png", audio: audio17 },
{ title: "heavy on my mind", desc: "rokiiyotakka", img: "2.png", audio: audio18 },
{ title: "jazzy type beat", desc: "rokiiyotakka", img: "2.png", audio: audio19 },
{ title: "love is in the air", desc: "rokiiyotakka", img: "2.png", audio: audio20 },
{ title: "pluggnb inspired", desc: "rokiiyotakka", img: "2.png", audio: audio21 },
{ title: "somber evenings", desc: "rokiiyotakka", img: "2.png", audio: audio22 },
{ title: "strawberry pomegranate", desc: "rokiiyotakka", img: "2.png", audio: audio23 },
{ title: "type shit", desc: "rokiiyotakka", img: "2.png", audio: audio24 },
{ title: "u make my heart tik tok", desc: "rokiiyotakka", img: "2.png", audio: audio25 },
{ title: "weeping isles", desc: "rokiiyotakka", img: "2.png", audio: audio26 },
{ title: "peach fuzz", desc: "rokiiyotakka", img: "2.png", audio: audio27 },
];
const popularSongs = [
{ title: "New Jazz Who Dis?", desc: "rokiiyotakka", img: "1.png", audio: audio28 },
{ title: "Sunset Boulevard", desc: "rokiiyotakka", img: "1.png", audio: audio29 },
{ title: "Street Cred", desc: "rokiiyotakka", img: "1.png", audio: audio30 },
{ title: "Back Of Da Clerb", desc: "rokiiyotakka", img: "1.png", audio: audio31 },
{ title: "Kii's Beat", desc: "rokiiyotakka", img: "1.png", audio: audio32 },
{ title: "Dancehall Attempt", desc: "rokiiyotakka", img: "1.png", audio: audio33 },
{ title: "Bomboclaat", desc: "rokiiyotakka", img: "1.png", audio: audio34 },
{ title: "Kii's Beat Pt2", desc: "rokiiyotakka", img: "1.png", audio: audio35 },
{ title: "In Da Clerb We All Fam", desc: "rokiiyotakka", img: "1.png", audio: audio36 },
{ title: "D-D-D-Drop It Again²", desc: "rokiiyotakka", img: "1.png", audio: audio37 },
{ title: "Hit My Line", desc: "rokiiyotakka", img: "1.png", audio: audio38 },
{ title: "Reservation At Eight", desc: "rokiiyotakka", img: "1.png", audio: audio39 },
{ title: "Island Tune", desc: "rokiiyotakka", img: "1.png", audio: audio40 },
{ title: "Welcome To The Eighties", desc: "rokiiyotakka", img: "1.png", audio: audio41 },
{ title: "Trappin'", desc: "rokiiyotakka", img: "1.png", audio: audio42 },
];
// 🔥 DASHBOARD COMPONENT
export default function Dashboard() {
const [currentSong, setCurrentSong] = useState(null);
const playSong = (song) => setCurrentSong(song);
return (
);
}
Код: Выделить всё
// src/pages/login.jsx
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { auth } from "../firebase";
import { signInWithEmailAndPassword } from "firebase/auth";
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();
const handleLogin = async () => {
setError("");
try {
await signInWithEmailAndPassword(auth, email, password);
navigate("/"); // go to dashboard
} catch (err) {
setError(err.message);
}
};
return (
Login
setEmail(e.target.value)}
/>
setPassword(e.target.value)}
/>
Login
{error &&
{error}
}
Don't have an account? Sign Up
);
}
Код: Выделить всё
// src/App.jsx
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Dashboard from "./dashboard";
import Login from "./login";
import Signup from "./signup";
import ProtectedRoute from "./protectedroute";
export default function App() {
return (
{/* Protect the dashboard */}
}
/>
);
}
Код: Выделить всё
// src/pages/signup.jsx
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { auth } from "../firebase";
import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth";
export default function Signup() {
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();
const handleSignup = async () => {
setError("");
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
// Set the display name
await updateProfile(userCredential.user, { displayName: username });
// Redirect to dashboard
navigate("/");
} catch (err) {
setError(err.message);
}
};
return (
Sign Up
setUsername(e.target.value)}
/>
setEmail(e.target.value)}
/>
setPassword(e.target.value)}
/>
Sign Up
{error &&
{error}
}
Already have an account? Login
);
}
Код: Выделить всё
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Dashboard from "./pages/dashboard";
import Login from "./pages/login";
import Signup from "./pages/signup";
import ProtectedRoute from "./pages/protectedroute";
import Navbar from "./components/navbar";
export default function App() {
return (
{/* Navbar is always rendered */}
{/* Protect dashboard */}
}
/>
{/* Auth pages */}
);
}
Код: Выделить всё
// src/main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./style.css";
ReactDOM.createRoot(document.getElementById("root")).render(
);
Подробнее здесь: https://stackoverflow.com/questions/798 ... tify-clone
Мобильная версия