├── .env ├── src ├── vite-env.d.ts ├── reducers │ ├── audioReducer.ts │ ├── searchReducer.ts │ ├── recommendationsReducer.ts │ ├── playingReducer.ts │ ├── libraryReducer.ts │ └── recentReducer.ts ├── components │ ├── Pages │ │ ├── Home │ │ │ ├── Header.tsx │ │ │ ├── Section.tsx │ │ │ └── index.tsx │ │ ├── Library │ │ │ └── index.tsx │ │ └── Search │ │ │ └── index.tsx │ ├── Music │ │ ├── AudioSound.tsx │ │ ├── SearchResult.tsx │ │ ├── MusicCard.tsx │ │ ├── SavedMusic.tsx │ │ └── RecentMusic.tsx │ ├── Navigation │ │ ├── RecentMusicList.tsx │ │ ├── Mobile.tsx │ │ └── Sidebar.tsx │ └── NowPlaying │ │ ├── Mobile │ │ ├── Footer.tsx │ │ └── Page.tsx │ │ └── Desktop │ │ ├── Footer.tsx │ │ └── Page.tsx ├── main.tsx ├── services │ ├── search.ts │ ├── library.ts │ ├── recent.ts │ └── recommendations.ts ├── store.ts ├── hooks │ └── index.ts ├── types.ts ├── context │ └── AudioContext.tsx ├── App.tsx └── index.css ├── images ├── image.png ├── Desktop.png └── phone-search.png ├── public └── favicon.ico ├── postcss.config.js ├── api ├── search.ts ├── types.ts ├── routes │ └── search.ts └── services │ └── search.ts ├── api.app.ts ├── vite.config.ts ├── api.devserver.ts ├── vercel.json ├── tsconfig.node.json ├── .gitignore ├── .eslintrc.cjs ├── tailwind.config.js ├── index.html ├── tsconfig.json ├── README.md ├── LICENSE ├── package.json └── tsconfig.tsnode.json /.env: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-podz/main/images/image.png -------------------------------------------------------------------------------- /images/Desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-podz/main/images/Desktop.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-podz/main/public/favicon.ico -------------------------------------------------------------------------------- /images/phone-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/code/app-podz/main/images/phone-search.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /api/search.ts: -------------------------------------------------------------------------------- 1 | import app from "../api.app.js"; 2 | import searchRouter from "./routes/search.js"; 3 | 4 | app.use("/api/search", searchRouter); 5 | 6 | export default app; 7 | -------------------------------------------------------------------------------- /api.app.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import cors from "cors"; 3 | 4 | const app = express(); 5 | app.use(cors()); 6 | app.use(express.json()); 7 | 8 | export default app; 9 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /api.devserver.ts: -------------------------------------------------------------------------------- 1 | import app from "./api.app.js"; 2 | import searchRouter from "./api/routes/search.js"; 3 | 4 | app.use("/api/search", searchRouter); 5 | 6 | app.listen(3001, () => { 7 | console.log("Server running at http://localhost:3001"); 8 | }); 9 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": [ 3 | { "src": "/search", "dest": "/", "status": 200 }, 4 | { "src": "/", "dest": "/", "status": 200 }, 5 | { "src": "/library", "dest": "/", "status": 200 }, 6 | { "src": "/api", "dest": "/api", "status": 200 } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /api/types.ts: -------------------------------------------------------------------------------- 1 | export interface SearchResult { 2 | id: string; 3 | title: string; 4 | author: string; 5 | image: string; 6 | duration: number; 7 | } 8 | 9 | export interface SearchResponse { 10 | id: string; 11 | title: { 12 | text: string; 13 | }; 14 | author: { 15 | name: string; 16 | }; 17 | thumbnails: { 18 | url: string; 19 | }[]; 20 | duration: { 21 | seconds: number; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/reducers/audioReducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | const initialState = { 4 | isPlaying: true, 5 | }; 6 | 7 | const audioSlice = createSlice({ 8 | name: "audio", 9 | initialState, 10 | reducers: { 11 | setIsPlaying(state, action) { 12 | return { ...state, isPlaying: action.payload }; 13 | }, 14 | }, 15 | }); 16 | 17 | export const { setIsPlaying } = audioSlice.actions; 18 | 19 | export default audioSlice.reducer; 20 | -------------------------------------------------------------------------------- /src/components /Pages/Home/Header.tsx: -------------------------------------------------------------------------------- 1 | import { RefObject } from "react"; 2 | 3 | const Header = ({ innerRef }: { innerRef: RefObject }) => { 4 | return ( 5 |
6 |

Hey, User!

7 |

8 | Check out this week's fresh releases. 9 |

10 |
11 | ); 12 | }; 13 | export default Header; 14 | -------------------------------------------------------------------------------- /api/routes/search.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import searchService from "../services/search.js"; 3 | 4 | const searchRouter = Router(); 5 | 6 | searchRouter.get("/", async (req, res) => { 7 | if (!(typeof req.query.query === "string")) return; 8 | const results = await searchService.search(req.query.query); 9 | if ("error" in results) return res.json(results); 10 | if ("single" in req.query) return res.json(results[0]); 11 | res.json(results); 12 | }); 13 | 14 | export default searchRouter; 15 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import { BrowserRouter as Router } from "react-router-dom"; 5 | import "./index.css"; 6 | import store from "./store.ts"; 7 | import { Provider } from "react-redux"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root")!).render( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /src/components /Music/AudioSound.tsx: -------------------------------------------------------------------------------- 1 | import { useAppSelector } from "../../hooks"; 2 | import { useAudioContext } from "../../context/AudioContext"; 3 | 4 | const AudioSound = () => { 5 | const playing = useAppSelector((state) => state.playing); 6 | const { audioRef } = useAudioContext(); 7 | 8 | return ( 9 |