From 3ea87b85d0b8ca0a65a05d0b1234ccfb5efd4515 Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sat, 24 Jun 2023 00:01:43 +0700 Subject: [PATCH 01/99] no msg --- src/App.module.scss | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/App.module.scss b/src/App.module.scss index ba08da0..d4126bd 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -25,15 +25,3 @@ height: 100%; width: 100%; } - -// .gutter { -// background-color: #fff; -// cursor: col-resize; -// } - -// .gutter.gutter-horizontal { -// /* background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); */ -// height: 100%; -// background-color: #fff; -// cursor: col-resize; -// } \ No newline at end of file From 61f2f3c4dda2829aa3e8b36a727a00009ba1aea9 Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sat, 24 Jun 2023 00:05:36 +0700 Subject: [PATCH 02/99] no msg --- src/App.module.scss | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/App.module.scss b/src/App.module.scss index 1ed9a57..a8395a9 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -25,14 +25,3 @@ width: 100%; } -// .gutter { -// background-color: #fff; -// cursor: col-resize; -// } - -// .gutter.gutter-horizontal { -// /* background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); */ -// height: 100%; -// background-color: #fff; -// cursor: col-resize; -// } \ No newline at end of file From 1ac394c0b1bd219fb74d2f1ddfb1fe046d46b37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tu=E1=BA=A5n=20=C4=90=E1=BA=B7ng?= <121254669+tuan204-dev@users.noreply.github.com> Date: Sat, 24 Jun 2023 00:11:18 +0700 Subject: [PATCH 03/99] Create codeql.yml --- .github/workflows/codeql.yml | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..4ea0a83 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,77 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '23 12 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 8cf20427f2c1850a8bb2f2766d37d4ec9db297cc Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sat, 24 Jun 2023 10:47:01 +0700 Subject: [PATCH 04/99] add NotFound Component --- src/App.module.scss | 1 + src/App.tsx | 38 ++++++---- src/assets/image/logo/logo.svg | 6 ++ src/components/NotFound/NotFound.module.scss | 77 ++++++++++++++++++++ src/components/NotFound/NotFound.tsx | 36 +++++++++ 5 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 src/assets/image/logo/logo.svg create mode 100644 src/components/NotFound/NotFound.module.scss create mode 100644 src/components/NotFound/NotFound.tsx diff --git a/src/App.module.scss b/src/App.module.scss index d4126bd..360bfca 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -16,6 +16,7 @@ border-radius: $panel-gap; overflow: hidden; overflow-y: auto; + position: relative; } diff --git a/src/App.tsx b/src/App.tsx index 0e5ba6d..541ac82 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,5 @@ -import { - Route, - Routes -} from 'react-router-dom' +import { useEffect, useState } from 'react' +import { Route, Routes, useLocation } from 'react-router-dom' import Split from 'react-split' import styles from './App.module.scss' import Sidebar from './components/Sidebar/Sidebar' @@ -10,8 +8,17 @@ import Artist from './pages/Artist/Artist' import Home from './pages/Home/Home' import Search from './pages/Search/Search' import './resizable.scss' +import NotFound from './components/NotFound/NotFound' function App() { + const { pathname } = useLocation() + const [showSidebar, setShowSidebar] = useState(true) + + useEffect(() => { + setShowSidebar(['/', '/search', 'artist'].includes(pathname)) + }, [pathname]) + console.log(showSidebar, pathname) + return (
- + {showSidebar && } -
- - } /> - } /> - } /> - -
+ {showSidebar ? ( +
+ + } /> + } /> + } /> + +
+ ) : ( + + )}
diff --git a/src/assets/image/logo/logo.svg b/src/assets/image/logo/logo.svg new file mode 100644 index 0000000..5f93661 --- /dev/null +++ b/src/assets/image/logo/logo.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/components/NotFound/NotFound.module.scss b/src/components/NotFound/NotFound.module.scss new file mode 100644 index 0000000..8ba830b --- /dev/null +++ b/src/components/NotFound/NotFound.module.scss @@ -0,0 +1,77 @@ +@import '../../scss/variable'; + +.wrapper { + background-color: $bg-base; + height: 100vh; + width: 100vw; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: absolute; + top: 0; + left: 0; + z-index: 99999; +} + +.logo { + height: 60px; + width: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: contain; +} + +.body { + display: flex; + flex-direction: column; + align-items: center; + padding: 40px; + + &__heading { + font-size: 48px; + font-weight: 700; + letter-spacing: -.7px; + margin: 4px 0 16px; + } + + &__sub-heading { + font-size: 16px; + color: $text-subdued; + margin: 0 0 40px; + } + + + &__home-btn { + font-size: 16px; + font-weight: 700; + text-decoration: none; + background: #fff; + border: 1px solid #878787; + border-radius: 48px; + color: #000; + display: inline-block; + line-height: 24px; + margin-bottom: 36px; + padding: 12px 32px; + text-align: center; + white-space: nowrap; + margin: 0 0 36px; + + &:hover { + transform: scale(1.04); + } + } + + &__help { + font-size: 16px; + color: #fff; + display: block; + font-weight: 700; + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} \ No newline at end of file diff --git a/src/components/NotFound/NotFound.tsx b/src/components/NotFound/NotFound.tsx new file mode 100644 index 0000000..0ded929 --- /dev/null +++ b/src/components/NotFound/NotFound.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import styles from './NotFound.module.scss' +import classNames from 'classnames/bind' +import logoImage from '@/assets/image/logo/logo.svg' +import { Link } from 'react-router-dom' + +const cx = classNames.bind(styles) + +const NotFound: React.FC = () => { + return ( +
+
+
+

Page not found

+

+ We can’t seem to find the page you are looking for. +

+ + Home + + + Help + +
+
+ ) +} + +export default NotFound From fe7422007ead9e82a3023af7a772eb848c937e4d Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sat, 24 Jun 2023 10:51:32 +0700 Subject: [PATCH 05/99] no msg --- src/components/NotFound/NotFound.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/NotFound/NotFound.tsx b/src/components/NotFound/NotFound.tsx index 0ded929..5b2fa10 100644 --- a/src/components/NotFound/NotFound.tsx +++ b/src/components/NotFound/NotFound.tsx @@ -2,7 +2,6 @@ import React from 'react' import styles from './NotFound.module.scss' import classNames from 'classnames/bind' import logoImage from '@/assets/image/logo/logo.svg' -import { Link } from 'react-router-dom' const cx = classNames.bind(styles) From e2c3b45886b3898e48b55acfa94b5889291d720c Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sat, 24 Jun 2023 13:59:41 +0700 Subject: [PATCH 06/99] add Footer component --- src/App.module.scss | 1 + src/App.tsx | 2 +- src/components/Footer/Footer.module.scss | 64 +++++++++++++++++++ src/components/Footer/Footer.tsx | 45 +++++++++++++ .../Footer/LinkGroup/LinkGroup.module.scss | 33 ++++++++++ src/components/Footer/LinkGroup/LinkGroup.tsx | 26 ++++++++ .../SidebarItem/SidebarItem.module.scss | 4 +- src/components/SidebarItem/SidebarItem.tsx | 6 +- src/constants/index.ts | 58 +++++++++++++++++ src/pages/Home/Home.module.scss | 8 +-- src/pages/Home/Home.tsx | 2 + src/pages/Search/Search.module.scss | 12 ++++ src/pages/Search/Search.tsx | 6 +- 13 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 src/components/Footer/Footer.module.scss create mode 100644 src/components/Footer/Footer.tsx create mode 100644 src/components/Footer/LinkGroup/LinkGroup.module.scss create mode 100644 src/components/Footer/LinkGroup/LinkGroup.tsx create mode 100644 src/constants/index.ts diff --git a/src/App.module.scss b/src/App.module.scss index 360bfca..548c027 100644 --- a/src/App.module.scss +++ b/src/App.module.scss @@ -13,6 +13,7 @@ .main { flex: 1; height: 100%; + background-color: $bg-base; border-radius: $panel-gap; overflow: hidden; overflow-y: auto; diff --git a/src/App.tsx b/src/App.tsx index 541ac82..849ea58 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,7 +11,7 @@ import './resizable.scss' import NotFound from './components/NotFound/NotFound' function App() { - const { pathname } = useLocation() + const { pathname, } = useLocation() const [showSidebar, setShowSidebar] = useState(true) useEffect(() => { diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss new file mode 100644 index 0000000..2365948 --- /dev/null +++ b/src/components/Footer/Footer.module.scss @@ -0,0 +1,64 @@ +@import '../../scss/mixin'; +@import '../../scss/variable'; + +.footer { + display: flex; + flex-direction: column; + padding: 8px 32px 40px; + + &__top { + display: flex; + justify-content: space-between; + + &-links { + display: flex; + flex-wrap: wrap; + } + + &-social-links { + display: flex; + column-gap: 16px; + + a { + @include button-icon(40px, rgb(41, 41, 41), rgb(114, 114, 114)); + + .icon { + color: $white; + font-size: 18px; + } + } + } + + } + + + &__bottom { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 26px; + border-top: 1px solid rgba($color: #fff, $alpha: .1); + + + &-links { + display: flex; + column-gap: 16px; + + &-item { + font-size: 14px; + color: $text-subdued; + text-decoration: none; + + &:hover { + color: $white; + } + } + } + + + &-copyright { + color: $text-subdued; + font-size: 14px; + } + } +} \ No newline at end of file diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx new file mode 100644 index 0000000..897ecfc --- /dev/null +++ b/src/components/Footer/Footer.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import styles from './Footer.module.scss' +import classNames from 'classnames/bind' +import { footerLinks, socialNetworkLinks, siteInfo } from '@/constants' +import LinkGroup from './LinkGroup/LinkGroup' + +const cx = classNames.bind(styles) + +const Footer: React.FC = () => { + return ( +
+ + +
+ ) +} + +export default Footer diff --git a/src/components/Footer/LinkGroup/LinkGroup.module.scss b/src/components/Footer/LinkGroup/LinkGroup.module.scss new file mode 100644 index 0000000..e8d2970 --- /dev/null +++ b/src/components/Footer/LinkGroup/LinkGroup.module.scss @@ -0,0 +1,33 @@ +@import '../../../scss/variable'; + +.wrapper { + display: flex; + flex-direction: column; + margin: 0 24px 32px 0; + min-width: 170px; + + .title { + font-size: 16px; + font-weight: 700; + color: $white; + } + + .list { + display: flex; + flex-direction: column; + color: $text-subdued; + + a { + color: inherit; + text-decoration: none; + font-size: 16px; + margin-top: 8px; + margin-bottom: 8px; + + &:hover { + color: $white; + text-decoration: underline; + } + } + } +} \ No newline at end of file diff --git a/src/components/Footer/LinkGroup/LinkGroup.tsx b/src/components/Footer/LinkGroup/LinkGroup.tsx new file mode 100644 index 0000000..4d7d430 --- /dev/null +++ b/src/components/Footer/LinkGroup/LinkGroup.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import styles from './LinkGroup.module.scss' +import classNames from 'classnames/bind' + +const cx = classNames.bind(styles) + +interface LinkGroupProps { + groupLink: { title: string; link: { title: string; url: string }[] } +} + +const LinkGroup: React.FC = ({ groupLink }) => { + return ( +
+

{groupLink.title}

+
+ {groupLink.link.map((item, index) => ( + + {item.title} + + ))} +
+
+ ) +} + +export default LinkGroup diff --git a/src/components/SidebarItem/SidebarItem.module.scss b/src/components/SidebarItem/SidebarItem.module.scss index 1a2f184..a6fd215 100644 --- a/src/components/SidebarItem/SidebarItem.module.scss +++ b/src/components/SidebarItem/SidebarItem.module.scss @@ -18,9 +18,7 @@ .thumbnail { height: 100%; aspect-ratio: 1 / 1; - background-position: center; - background-repeat: no-repeat; - background-size: cover; + object-fit: cover; border-radius: 4px; } diff --git a/src/components/SidebarItem/SidebarItem.tsx b/src/components/SidebarItem/SidebarItem.tsx index fbd9846..1b5efcb 100644 --- a/src/components/SidebarItem/SidebarItem.tsx +++ b/src/components/SidebarItem/SidebarItem.tsx @@ -6,8 +6,8 @@ const cx = classNames.bind(styles) interface SidebarItemProps { type: 'playlist' | 'artist' | 'album' - thumbnail: string | null - name: string | null + thumbnail: string | undefined + name: string | undefined } @@ -22,7 +22,7 @@ const SidebarItem: FC = (props) => { return (
-
+

{name}

diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..7766989 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,58 @@ +import { BsFacebook } from 'react-icons/bs' +import { FaInstagram, FaGithub } from 'react-icons/fa' + +export const footerLinks = [ + { + title: 'Company', + link: [ + { title: 'About', url: '/' }, + { title: 'Jobs', url: '/' }, + { title: 'For the Record', url: '/' }, + ], + }, + { + title: 'Communities', + link: [ + { title: 'For Artists', url: '/' }, + { title: 'Developers', url: '/' }, + { title: 'Advertising', url: '/' }, + { title: 'Investors', url: '/' }, + { title: 'Vendors', url: '/' }, + { title: 'Spotify for Work', url: '/' }, + ], + }, + { + title: 'Useful Links', + link: [ + { title: 'Support', url: '/' }, + { title: 'Free Mobile App', url: '/' }, + ], + }, +] + +export const socialNetworkLinks = [ + { + title: 'Facebook', + link: 'https://www.facebook.com/tuan204.dev', + icon: BsFacebook, + }, + { + title: 'Instagram', + link: 'https://www.instagram.com/tuan204.dev/', + icon: FaInstagram, + }, + { + title: 'Github', + link: 'https://github.com/tuan204-dev', + icon: FaGithub, + }, +] + +export const siteInfo = [ + { title: 'Legal', url: '/' }, + { title: 'Privacy Center', url: '/' }, + { title: 'Privacy Policy', url: '/' }, + { title: 'Cookies', url: '/' }, + { title: 'About Ads', url: '/' }, + { title: 'Accessibility', url: '/' }, +] diff --git a/src/pages/Home/Home.module.scss b/src/pages/Home/Home.module.scss index cfef0dd..cc9891d 100644 --- a/src/pages/Home/Home.module.scss +++ b/src/pages/Home/Home.module.scss @@ -2,18 +2,16 @@ height: 100%; position: relative; overflow: hidden; - } .body { height: 100%; overflow: hidden; overflow-y: scroll; + padding-bottom: 32px; + &::-webkit-scrollbar { display: none; } -} - - - +} \ No newline at end of file diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index f8cdfaa..cde8046 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -3,6 +3,7 @@ import Navbar from '@/components/Navbar/Navbar' import classNames from 'classnames/bind' import { useState } from 'react' import styles from './Home.module.scss' +import Footer from '@/components/Footer/Footer' const cx = classNames.bind(styles) @@ -28,6 +29,7 @@ const Home = () => {
handleScroll(e)} className={cx('body')}>
+
) diff --git a/src/pages/Search/Search.module.scss b/src/pages/Search/Search.module.scss index cc20682..da6e219 100644 --- a/src/pages/Search/Search.module.scss +++ b/src/pages/Search/Search.module.scss @@ -3,4 +3,16 @@ position: relative; overflow: hidden; +} + +.body { + height: 100%; + overflow: hidden; + overflow-y: scroll; + padding-bottom: 32px; + + + &::-webkit-scrollbar { + display: none; + } } \ No newline at end of file diff --git a/src/pages/Search/Search.tsx b/src/pages/Search/Search.tsx index 3fb5d17..6d6a6a9 100644 --- a/src/pages/Search/Search.tsx +++ b/src/pages/Search/Search.tsx @@ -2,6 +2,7 @@ import Navbar from '@/components/Navbar/Navbar' import classNames from 'classnames/bind' import React, { FC, useState } from 'react' import styles from './Search.module.scss' +import Footer from '@/components/Footer/Footer' const cx = classNames.bind(styles) @@ -16,7 +17,10 @@ const Search: FC = () => { return (
-
+
+
+
+
) } From e67efa086baa51dd801f7eb763d8eadc9561b5de Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Sun, 25 Jun 2023 01:12:19 +0700 Subject: [PATCH 07/99] add SearchBanner component and adjust useComponentSize dependencies --- public/data/bannerSearch.json | 332 ++++++++++++++++++ public/data/initSongs.json | 4 +- .../img}/search-banner/image (1).jpg | Bin .../img}/search-banner/image (10).jpg | Bin .../img}/search-banner/image (11).jpg | Bin .../img}/search-banner/image (12).jpg | Bin .../img}/search-banner/image (13).jpg | Bin .../img}/search-banner/image (14).jpg | Bin .../img}/search-banner/image (15).jpg | Bin .../img}/search-banner/image (16).jpg | Bin .../img}/search-banner/image (17).jpg | Bin .../img}/search-banner/image (18).jpg | Bin .../img}/search-banner/image (19).jpg | Bin .../img}/search-banner/image (2).jpg | Bin .../img}/search-banner/image (20).jpg | Bin .../img}/search-banner/image (21).jpg | Bin .../img}/search-banner/image (22).jpg | Bin .../img}/search-banner/image (23).jpg | Bin .../img}/search-banner/image (24).jpg | Bin .../img}/search-banner/image (25).jpg | Bin .../img}/search-banner/image (26).jpg | Bin .../img}/search-banner/image (27).jpg | Bin .../img}/search-banner/image (28).jpg | Bin .../img}/search-banner/image (29).jpg | Bin .../img}/search-banner/image (3).jpg | Bin .../img}/search-banner/image (30).jpg | Bin .../img}/search-banner/image (31).jpg | Bin .../img}/search-banner/image (32).jpg | Bin .../img}/search-banner/image (33).jpg | Bin .../img}/search-banner/image (34).jpg | Bin .../img}/search-banner/image (35).jpg | Bin .../img}/search-banner/image (36).jpg | Bin .../img}/search-banner/image (37).jpg | Bin .../img}/search-banner/image (38).jpg | Bin .../img}/search-banner/image (39).jpg | Bin .../img}/search-banner/image (4).jpg | Bin .../img}/search-banner/image (40).jpg | Bin .../img}/search-banner/image (41).jpg | Bin .../img}/search-banner/image (42).jpg | Bin .../img}/search-banner/image (43).jpg | Bin .../img}/search-banner/image (44).jpg | Bin .../img}/search-banner/image (45).jpg | Bin .../img}/search-banner/image (46).jpg | Bin .../img}/search-banner/image (47).jpg | Bin .../img}/search-banner/image (48).jpg | Bin .../img}/search-banner/image (49).jpg | Bin .../img}/search-banner/image (5).jpg | Bin .../img}/search-banner/image (50).jpg | Bin .../img}/search-banner/image (51).jpg | Bin .../img}/search-banner/image (52).jpg | Bin .../img}/search-banner/image (53).jpg | Bin .../img}/search-banner/image (54).jpg | Bin .../img}/search-banner/image (55).jpg | Bin .../img}/search-banner/image (6).jpg | Bin .../img}/search-banner/image (7).jpg | Bin .../img}/search-banner/image (8).jpg | Bin .../img}/search-banner/image (9).jpg | Bin .../img}/search-banner/index.ts | 0 src/App.tsx | 4 +- src/assets/icons/index.tsx | 80 +++++ .../BannerItem/BannerItem.module.scss | 38 ++ .../BannerSearch/BannerItem/BannerItem.tsx | 31 ++ .../BannerSearch/SearchBanner.module.scss | 19 + src/components/BannerSearch/SearchBanner.tsx | 57 +++ src/components/Footer/Footer.module.scss | 2 + src/components/Footer/Footer.tsx | 4 +- src/components/Greeting/Greeting.module.scss | 1 + src/components/Navbar/Navbar.module.scss | 5 +- src/components/Sidebar/Library/Library.tsx | 4 +- src/components/Sidebar/Nav/Nav.tsx | 15 +- src/contexts/MainLayoutContext.tsx | 1 + src/hooks/useComponentSize.ts | 3 +- src/index.scss | 4 + src/pages/Home/Home.module.scss | 9 +- src/pages/Search/Search.module.scss | 2 +- src/pages/Search/Search.tsx | 3 +- src/scss/_global.scss | 3 + types.ts | 5 + 78 files changed, 600 insertions(+), 26 deletions(-) create mode 100644 public/data/bannerSearch.json rename {src/assets/image => public/img}/search-banner/image (1).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (10).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (11).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (12).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (13).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (14).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (15).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (16).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (17).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (18).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (19).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (2).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (20).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (21).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (22).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (23).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (24).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (25).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (26).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (27).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (28).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (29).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (3).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (30).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (31).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (32).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (33).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (34).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (35).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (36).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (37).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (38).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (39).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (4).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (40).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (41).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (42).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (43).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (44).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (45).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (46).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (47).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (48).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (49).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (5).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (50).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (51).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (52).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (53).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (54).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (55).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (6).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (7).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (8).jpg (100%) rename {src/assets/image => public/img}/search-banner/image (9).jpg (100%) rename {src/assets/image => public/img}/search-banner/index.ts (100%) create mode 100644 src/assets/icons/index.tsx create mode 100644 src/components/BannerSearch/BannerItem/BannerItem.module.scss create mode 100644 src/components/BannerSearch/BannerItem/BannerItem.tsx create mode 100644 src/components/BannerSearch/SearchBanner.module.scss create mode 100644 src/components/BannerSearch/SearchBanner.tsx diff --git a/public/data/bannerSearch.json b/public/data/bannerSearch.json new file mode 100644 index 0000000..ad10230 --- /dev/null +++ b/public/data/bannerSearch.json @@ -0,0 +1,332 @@ +[ + { + "title": "Podcasts", + "imageUrl": "https://i.scdn.co/image/ab6765630000ba8a9417936d038e7a2f8dee2554", + "bgColor": "rgb(225, 51, 0)" + }, + { + "title": "Live Events", + "imageUrl": "https://concerts.spotifycdn.com/images/live-events_category-image.jpg", + "bgColor": "rgb(115, 88, 255)" + }, + { + "title": "Made For You", + "imageUrl": "https://t.scdn.co/images/ea364e99656e46a096ea1df50f581efe", + "bgColor": "rgb(30, 50, 100)" + }, + { + "title": "New releases", + "imageUrl": "https://i.scdn.co/image/ab67706f000000027ea4d505212b9de1f72c5112", + "bgColor": "rgb(232, 17, 91)" + }, + { + "title": "Vietnamese", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf55dfb53724670e4db6cee444", + "bgColor": "rgb(96, 129, 8)" + }, + { + "title": "Pop", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafa862ab80dd85682b37c4e768", + "bgColor": "rgb(20, 138, 8)" + }, + { + "title": "K-pop", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf013ee3c983e6f60bf28bad5a", + "bgColor": "rgb(20, 138, 8)" + }, + { + "title": "Hip-Hop", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf7e11c8413dc33c00740579c1", + "bgColor": "rgb(188, 89, 0)" + }, + { + "title": "Charts", + "imageUrl": "https://charts-images.scdn.co/assets/locale_en/regional/weekly/region_global_default.jpg", + "bgColor": "rgb(141, 103, 171)" + }, + { + "title": "Fresh Finds", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafcc1499bbb8565f490858c2bc", + "bgColor": "rgb(255, 0, 144)" + }, + { + "title": "EQUAL", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf9ed6e364e8839210dc4dbff7", + "bgColor": "rgb(5, 105, 82)" + }, + { + "title": "GLOW", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf50cfe3fbd3a9fb8810da45ea", + "bgColor": "rgb(13, 115, 236)" + }, + { + "title": "RADAR", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafdb2aa9c7caea42f900721497", + "bgColor": "rgb(119, 119, 119)" + }, + { + "title": "Discover", + "imageUrl": "https://t.scdn.co/images/d0fb2ab104dc4846bdc56d72b0b0d785.jpeg", + "bgColor": "rgb(141, 103, 171)" + }, + { + "title": "Karaoke", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf8eb00a9a2c00093ccde516c6", + "bgColor": "rgb(80, 55, 80)" + }, + { + "title": "Mood", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf271f9d895003c5f5561c1354", + "bgColor": "rgb(225, 17, 140)" + }, + { + "title": "Rock", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafae7e69beb88f16969641b53e", + "bgColor": "rgb(233, 20, 41)" + }, + { + "title": "Latin", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafa59f90c077c9f618fd0dde30", + "bgColor": "rgb(225, 17, 140)" + }, + { + "title": "Dance/Electronic", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafdfdaac1cf9574a196ca25196", + "bgColor": "rgb(216, 64, 0)" + }, + { + "title": "Indie", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafa1a252e3a815b65778d8c2aa", + "bgColor": "rgb(233, 20, 41)" + }, + { + "title": "Workout", + "imageUrl": "https://i.scdn.co/image/ab67706f000000029249b35f23fb596b6f006a15", + "bgColor": "rgb(119, 119, 119)" + }, + { + "title": "Country", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafc0d2222b4c6441930e1a386e", + "bgColor": "rgb(216, 64, 0)" + }, + { + "title": "R&B", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafbe6a6e705e1a71117c2d0c2c", + "bgColor": "rgb(220, 20, 140)" + }, + { + "title": "Chill", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf47e942f5bea637f4f4760170", + "bgColor": "rgb(216, 64, 0)" + }, + { + "title": "Sleep", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafdfdaac1cf9574a196ca25196", + "bgColor": "rgb(30, 50, 100)" + }, + { + "title": "Party", + "imageUrl": "https://i.scdn.co/image/ab67706f00000002b70e0223f544b1faa2e95ed0", + "bgColor": "rgb(83, 122, 161)" + }, + { + "title": "At Home", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafc0d2222b4c6441930e1a386e", + "bgColor": "rgb(81, 121, 161)" + }, + { + "title": "Decades", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafcbf80f8ea695536eace4fd2c", + "bgColor": "rgb(186, 93, 7)" + }, + { + "title": "Love", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafe914a07d20cec7a65e2e5dad", + "bgColor": "rgb(230, 30, 50)" + }, + { + "title": "Metal", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caff005a355830c374754d32868", + "bgColor": "rgb(233, 20, 41)" + }, + { + "title": "Jazz", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafe289743024639ea8f202364d", + "bgColor": "rgb(119, 119, 119)" + }, + { + "title": "Trending", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf1867113f5218598847550acd", + "bgColor": "rgb(176, 40, 151)" + }, + { + "title": "Classical", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf12809992dfc5b318892ea07b", + "bgColor": "rgb(125, 75, 50)" + }, + { + "title": "Acoustic", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafcc70a3c2e4c71398708bdc4a", + "bgColor": "rgb(188, 89, 0)" + }, + { + "title": "Focus", + "imageUrl": "https://i.scdn.co/image/ab67706f00000002e4eadd417a05b2546e866934", + "bgColor": "rgb(80, 55, 80)" + }, + { + "title": "Soul", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafd82e2c83fe100a89e9cbb2a2", + "bgColor": "rgb(220, 20, 140)" + }, + { + "title": "Family", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf8a04560a209b3f32165ea8a2", + "bgColor": "rgb(141, 103, 171)" + }, + { + "title": "Gaming", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf26dd3719e8824756914ae61f", + "bgColor": "rgb(232, 17, 91)" + }, + { + "title": "Anime", + "imageUrl": "https://i.scdn.co/image/ab67706f00000002c19c5f13f8b3ff2d73ff00bc", + "bgColor": "rgb(228, 29, 99)" + }, + { + "title": "TV & Movies", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafb4c4523336133ec3c7fd1744", + "bgColor": "rgb(175, 40, 150)" + }, + { + "title": "Disney", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf89f004e1bf07dca45ce9c64c", + "bgColor": "rgb(13, 114, 234)" + }, + { + "title": "Netflix", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf0b0c71c920d6a745461ada69", + "bgColor": "rgb(235, 30, 50)" + }, + { + "title": "Instrumental", + "imageUrl": "https://i.scdn.co/image/ab67706f000000028ed1a5002b96c2ea882541b2", + "bgColor": "rgb(83, 122, 161)" + }, + { + "title": "Wellness", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf8dec632effd9735fa8aba06e", + "bgColor": "rgb(165, 103, 82)" + }, + { + "title": "Punk", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafb2cdd7a95b0a5444aa15cfb5", + "bgColor": "rgb(30, 50, 100)" + }, + { + "title": "Anime", + "imageUrl": "https://i.scdn.co/image/ab67706f00000002c19c5f13f8b3ff2d73ff00bc", + "bgColor": "rgb(228, 29, 99)" + }, + { + "title": "TV & Movies", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafb4c4523336133ec3c7fd1744", + "bgColor": "rgb(175, 40, 150)" + }, + { + "title": "Disney", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf89f004e1bf07dca45ce9c64c", + "bgColor": "rgb(13, 114, 234)" + }, + { + "title": "Netflix", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf0b0c71c920d6a745461ada69", + "bgColor": "rgb(235, 30, 50)" + }, + { + "title": "Instrumental", + "imageUrl": "https://i.scdn.co/image/ab67706f000000028ed1a5002b96c2ea882541b2", + "bgColor": "rgb(83, 122, 161)" + }, + { + "title": "Wellness", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf8dec632effd9735fa8aba06e", + "bgColor": "rgb(165, 103, 82)" + }, + { + "title": "Punk", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafb2cdd7a95b0a5444aa15cfb5", + "bgColor": "rgb(30, 50, 100)" + }, + { + "title": "Ambient", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafa6ee95dc83af715115f40522", + "bgColor": "rgb(71, 125, 149)" + }, + { + "title": "Blues", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caff22ac5cab318d550b593ffac", + "bgColor": "rgb(176, 98, 57)" + }, + { + "title": "Dining", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafe53d71d0920a4f1f441d803c", + "bgColor": "rgb(186, 93, 7)" + }, + { + "title": "Alternative", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafda178a834e4f87371e9fa543", + "bgColor": "rgb(233, 20, 41)" + }, + { + "title": "Travel", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafe53d71d0920a4f1f441d803c", + "bgColor": "rgb(13, 114, 237)" + }, + { + "title": "Caribbean", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafe53d71d0920a4f1f441d803c", + "bgColor": "rgb(13, 115, 236)" + }, + { + "title": "Afro", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf8ba1febbb4f77336b6f9aace", + "bgColor": "rgb(216, 64, 0)" + }, + { + "title": "Songwriters", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf04faccb4f5e1828600921f37", + "bgColor": "rgb(140, 25, 50)" + }, + { + "title": "Noise", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafb973ab1288f74f333e7e2e22", + "bgColor": "rgb(71, 125, 149)" + }, + { + "title": "Disco", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafbbf84e26e69a78883118913c", + "bgColor": "rgb(230, 30, 50)" + }, + { + "title": "Legends", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005cafbb0e4ea229824157eee7467d", + "bgColor": "rgb(20, 138, 8)" + }, + { + "title": "Spotify Singles", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf2b1ff59a971dd399dea96009", + "bgColor": "rgb(119, 119, 119)" + }, + { + "title": "Summer", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf14030380532b34badbf0a229", + "bgColor": "rgb(39, 133, 106)" + }, + { + "title": "Tastemakers", + "imageUrl": "https://i.scdn.co/image/ab67fb8200005caf097a46192e6bb67e52cdff60", + "bgColor": "rgb(232, 17, 91)" + } +] diff --git a/public/data/initSongs.json b/public/data/initSongs.json index 55731f5..fadde7d 100644 --- a/public/data/initSongs.json +++ b/public/data/initSongs.json @@ -339,4 +339,6 @@ "limit": 6 } } -} \ No newline at end of file +} + + diff --git a/src/assets/image/search-banner/image (1).jpg b/public/img/search-banner/image (1).jpg similarity index 100% rename from src/assets/image/search-banner/image (1).jpg rename to public/img/search-banner/image (1).jpg diff --git a/src/assets/image/search-banner/image (10).jpg b/public/img/search-banner/image (10).jpg similarity index 100% rename from src/assets/image/search-banner/image (10).jpg rename to public/img/search-banner/image (10).jpg diff --git a/src/assets/image/search-banner/image (11).jpg b/public/img/search-banner/image (11).jpg similarity index 100% rename from src/assets/image/search-banner/image (11).jpg rename to public/img/search-banner/image (11).jpg diff --git a/src/assets/image/search-banner/image (12).jpg b/public/img/search-banner/image (12).jpg similarity index 100% rename from src/assets/image/search-banner/image (12).jpg rename to public/img/search-banner/image (12).jpg diff --git a/src/assets/image/search-banner/image (13).jpg b/public/img/search-banner/image (13).jpg similarity index 100% rename from src/assets/image/search-banner/image (13).jpg rename to public/img/search-banner/image (13).jpg diff --git a/src/assets/image/search-banner/image (14).jpg b/public/img/search-banner/image (14).jpg similarity index 100% rename from src/assets/image/search-banner/image (14).jpg rename to public/img/search-banner/image (14).jpg diff --git a/src/assets/image/search-banner/image (15).jpg b/public/img/search-banner/image (15).jpg similarity index 100% rename from src/assets/image/search-banner/image (15).jpg rename to public/img/search-banner/image (15).jpg diff --git a/src/assets/image/search-banner/image (16).jpg b/public/img/search-banner/image (16).jpg similarity index 100% rename from src/assets/image/search-banner/image (16).jpg rename to public/img/search-banner/image (16).jpg diff --git a/src/assets/image/search-banner/image (17).jpg b/public/img/search-banner/image (17).jpg similarity index 100% rename from src/assets/image/search-banner/image (17).jpg rename to public/img/search-banner/image (17).jpg diff --git a/src/assets/image/search-banner/image (18).jpg b/public/img/search-banner/image (18).jpg similarity index 100% rename from src/assets/image/search-banner/image (18).jpg rename to public/img/search-banner/image (18).jpg diff --git a/src/assets/image/search-banner/image (19).jpg b/public/img/search-banner/image (19).jpg similarity index 100% rename from src/assets/image/search-banner/image (19).jpg rename to public/img/search-banner/image (19).jpg diff --git a/src/assets/image/search-banner/image (2).jpg b/public/img/search-banner/image (2).jpg similarity index 100% rename from src/assets/image/search-banner/image (2).jpg rename to public/img/search-banner/image (2).jpg diff --git a/src/assets/image/search-banner/image (20).jpg b/public/img/search-banner/image (20).jpg similarity index 100% rename from src/assets/image/search-banner/image (20).jpg rename to public/img/search-banner/image (20).jpg diff --git a/src/assets/image/search-banner/image (21).jpg b/public/img/search-banner/image (21).jpg similarity index 100% rename from src/assets/image/search-banner/image (21).jpg rename to public/img/search-banner/image (21).jpg diff --git a/src/assets/image/search-banner/image (22).jpg b/public/img/search-banner/image (22).jpg similarity index 100% rename from src/assets/image/search-banner/image (22).jpg rename to public/img/search-banner/image (22).jpg diff --git a/src/assets/image/search-banner/image (23).jpg b/public/img/search-banner/image (23).jpg similarity index 100% rename from src/assets/image/search-banner/image (23).jpg rename to public/img/search-banner/image (23).jpg diff --git a/src/assets/image/search-banner/image (24).jpg b/public/img/search-banner/image (24).jpg similarity index 100% rename from src/assets/image/search-banner/image (24).jpg rename to public/img/search-banner/image (24).jpg diff --git a/src/assets/image/search-banner/image (25).jpg b/public/img/search-banner/image (25).jpg similarity index 100% rename from src/assets/image/search-banner/image (25).jpg rename to public/img/search-banner/image (25).jpg diff --git a/src/assets/image/search-banner/image (26).jpg b/public/img/search-banner/image (26).jpg similarity index 100% rename from src/assets/image/search-banner/image (26).jpg rename to public/img/search-banner/image (26).jpg diff --git a/src/assets/image/search-banner/image (27).jpg b/public/img/search-banner/image (27).jpg similarity index 100% rename from src/assets/image/search-banner/image (27).jpg rename to public/img/search-banner/image (27).jpg diff --git a/src/assets/image/search-banner/image (28).jpg b/public/img/search-banner/image (28).jpg similarity index 100% rename from src/assets/image/search-banner/image (28).jpg rename to public/img/search-banner/image (28).jpg diff --git a/src/assets/image/search-banner/image (29).jpg b/public/img/search-banner/image (29).jpg similarity index 100% rename from src/assets/image/search-banner/image (29).jpg rename to public/img/search-banner/image (29).jpg diff --git a/src/assets/image/search-banner/image (3).jpg b/public/img/search-banner/image (3).jpg similarity index 100% rename from src/assets/image/search-banner/image (3).jpg rename to public/img/search-banner/image (3).jpg diff --git a/src/assets/image/search-banner/image (30).jpg b/public/img/search-banner/image (30).jpg similarity index 100% rename from src/assets/image/search-banner/image (30).jpg rename to public/img/search-banner/image (30).jpg diff --git a/src/assets/image/search-banner/image (31).jpg b/public/img/search-banner/image (31).jpg similarity index 100% rename from src/assets/image/search-banner/image (31).jpg rename to public/img/search-banner/image (31).jpg diff --git a/src/assets/image/search-banner/image (32).jpg b/public/img/search-banner/image (32).jpg similarity index 100% rename from src/assets/image/search-banner/image (32).jpg rename to public/img/search-banner/image (32).jpg diff --git a/src/assets/image/search-banner/image (33).jpg b/public/img/search-banner/image (33).jpg similarity index 100% rename from src/assets/image/search-banner/image (33).jpg rename to public/img/search-banner/image (33).jpg diff --git a/src/assets/image/search-banner/image (34).jpg b/public/img/search-banner/image (34).jpg similarity index 100% rename from src/assets/image/search-banner/image (34).jpg rename to public/img/search-banner/image (34).jpg diff --git a/src/assets/image/search-banner/image (35).jpg b/public/img/search-banner/image (35).jpg similarity index 100% rename from src/assets/image/search-banner/image (35).jpg rename to public/img/search-banner/image (35).jpg diff --git a/src/assets/image/search-banner/image (36).jpg b/public/img/search-banner/image (36).jpg similarity index 100% rename from src/assets/image/search-banner/image (36).jpg rename to public/img/search-banner/image (36).jpg diff --git a/src/assets/image/search-banner/image (37).jpg b/public/img/search-banner/image (37).jpg similarity index 100% rename from src/assets/image/search-banner/image (37).jpg rename to public/img/search-banner/image (37).jpg diff --git a/src/assets/image/search-banner/image (38).jpg b/public/img/search-banner/image (38).jpg similarity index 100% rename from src/assets/image/search-banner/image (38).jpg rename to public/img/search-banner/image (38).jpg diff --git a/src/assets/image/search-banner/image (39).jpg b/public/img/search-banner/image (39).jpg similarity index 100% rename from src/assets/image/search-banner/image (39).jpg rename to public/img/search-banner/image (39).jpg diff --git a/src/assets/image/search-banner/image (4).jpg b/public/img/search-banner/image (4).jpg similarity index 100% rename from src/assets/image/search-banner/image (4).jpg rename to public/img/search-banner/image (4).jpg diff --git a/src/assets/image/search-banner/image (40).jpg b/public/img/search-banner/image (40).jpg similarity index 100% rename from src/assets/image/search-banner/image (40).jpg rename to public/img/search-banner/image (40).jpg diff --git a/src/assets/image/search-banner/image (41).jpg b/public/img/search-banner/image (41).jpg similarity index 100% rename from src/assets/image/search-banner/image (41).jpg rename to public/img/search-banner/image (41).jpg diff --git a/src/assets/image/search-banner/image (42).jpg b/public/img/search-banner/image (42).jpg similarity index 100% rename from src/assets/image/search-banner/image (42).jpg rename to public/img/search-banner/image (42).jpg diff --git a/src/assets/image/search-banner/image (43).jpg b/public/img/search-banner/image (43).jpg similarity index 100% rename from src/assets/image/search-banner/image (43).jpg rename to public/img/search-banner/image (43).jpg diff --git a/src/assets/image/search-banner/image (44).jpg b/public/img/search-banner/image (44).jpg similarity index 100% rename from src/assets/image/search-banner/image (44).jpg rename to public/img/search-banner/image (44).jpg diff --git a/src/assets/image/search-banner/image (45).jpg b/public/img/search-banner/image (45).jpg similarity index 100% rename from src/assets/image/search-banner/image (45).jpg rename to public/img/search-banner/image (45).jpg diff --git a/src/assets/image/search-banner/image (46).jpg b/public/img/search-banner/image (46).jpg similarity index 100% rename from src/assets/image/search-banner/image (46).jpg rename to public/img/search-banner/image (46).jpg diff --git a/src/assets/image/search-banner/image (47).jpg b/public/img/search-banner/image (47).jpg similarity index 100% rename from src/assets/image/search-banner/image (47).jpg rename to public/img/search-banner/image (47).jpg diff --git a/src/assets/image/search-banner/image (48).jpg b/public/img/search-banner/image (48).jpg similarity index 100% rename from src/assets/image/search-banner/image (48).jpg rename to public/img/search-banner/image (48).jpg diff --git a/src/assets/image/search-banner/image (49).jpg b/public/img/search-banner/image (49).jpg similarity index 100% rename from src/assets/image/search-banner/image (49).jpg rename to public/img/search-banner/image (49).jpg diff --git a/src/assets/image/search-banner/image (5).jpg b/public/img/search-banner/image (5).jpg similarity index 100% rename from src/assets/image/search-banner/image (5).jpg rename to public/img/search-banner/image (5).jpg diff --git a/src/assets/image/search-banner/image (50).jpg b/public/img/search-banner/image (50).jpg similarity index 100% rename from src/assets/image/search-banner/image (50).jpg rename to public/img/search-banner/image (50).jpg diff --git a/src/assets/image/search-banner/image (51).jpg b/public/img/search-banner/image (51).jpg similarity index 100% rename from src/assets/image/search-banner/image (51).jpg rename to public/img/search-banner/image (51).jpg diff --git a/src/assets/image/search-banner/image (52).jpg b/public/img/search-banner/image (52).jpg similarity index 100% rename from src/assets/image/search-banner/image (52).jpg rename to public/img/search-banner/image (52).jpg diff --git a/src/assets/image/search-banner/image (53).jpg b/public/img/search-banner/image (53).jpg similarity index 100% rename from src/assets/image/search-banner/image (53).jpg rename to public/img/search-banner/image (53).jpg diff --git a/src/assets/image/search-banner/image (54).jpg b/public/img/search-banner/image (54).jpg similarity index 100% rename from src/assets/image/search-banner/image (54).jpg rename to public/img/search-banner/image (54).jpg diff --git a/src/assets/image/search-banner/image (55).jpg b/public/img/search-banner/image (55).jpg similarity index 100% rename from src/assets/image/search-banner/image (55).jpg rename to public/img/search-banner/image (55).jpg diff --git a/src/assets/image/search-banner/image (6).jpg b/public/img/search-banner/image (6).jpg similarity index 100% rename from src/assets/image/search-banner/image (6).jpg rename to public/img/search-banner/image (6).jpg diff --git a/src/assets/image/search-banner/image (7).jpg b/public/img/search-banner/image (7).jpg similarity index 100% rename from src/assets/image/search-banner/image (7).jpg rename to public/img/search-banner/image (7).jpg diff --git a/src/assets/image/search-banner/image (8).jpg b/public/img/search-banner/image (8).jpg similarity index 100% rename from src/assets/image/search-banner/image (8).jpg rename to public/img/search-banner/image (8).jpg diff --git a/src/assets/image/search-banner/image (9).jpg b/public/img/search-banner/image (9).jpg similarity index 100% rename from src/assets/image/search-banner/image (9).jpg rename to public/img/search-banner/image (9).jpg diff --git a/src/assets/image/search-banner/index.ts b/public/img/search-banner/index.ts similarity index 100% rename from src/assets/image/search-banner/index.ts rename to public/img/search-banner/index.ts diff --git a/src/App.tsx b/src/App.tsx index 849ea58..6142d5a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,13 +11,13 @@ import './resizable.scss' import NotFound from './components/NotFound/NotFound' function App() { - const { pathname, } = useLocation() + const { pathname } = useLocation() const [showSidebar, setShowSidebar] = useState(true) useEffect(() => { setShowSidebar(['/', '/search', 'artist'].includes(pathname)) }, [pathname]) - console.log(showSidebar, pathname) + // console.log(showSidebar, pathname) return (
diff --git a/src/assets/icons/index.tsx b/src/assets/icons/index.tsx new file mode 100644 index 0000000..902dde3 --- /dev/null +++ b/src/assets/icons/index.tsx @@ -0,0 +1,80 @@ +export function SearchActiveIcon() { + return ( + + ) +} + +export function SearchIcon() { + return ( + + ) +} + +export function HomeActiveIcon() { + return ( + + ) +} + +export function HomeIcon() { + return ( + + ) +} + +export function LibraryIcon() { + return ( + + ) +} diff --git a/src/components/BannerSearch/BannerItem/BannerItem.module.scss b/src/components/BannerSearch/BannerItem/BannerItem.module.scss new file mode 100644 index 0000000..1b1c005 --- /dev/null +++ b/src/components/BannerSearch/BannerItem/BannerItem.module.scss @@ -0,0 +1,38 @@ +.main { + // width: 100%; + // min-width: 150px; + // max-width: 230px; + aspect-ratio: 1 / 1; + border-radius: 8px; + position: relative; + overflow: hidden; +} + +.wrapper { + width: 100%; + height: 100%; + border-radius: 8px; + position: relative; + overflow: hidden; + + .title { + font-size: 24px; + margin: 16px; + text-decoration: none !important; + color: #fff; + } + + .img { + width: 100px; + aspect-ratio: 1 / 1; + position: absolute; + rotate: 25deg; + bottom: -14px; + right: -10px; + + img { + width: 100%; + object-fit: cover; + } + } +} \ No newline at end of file diff --git a/src/components/BannerSearch/BannerItem/BannerItem.tsx b/src/components/BannerSearch/BannerItem/BannerItem.tsx new file mode 100644 index 0000000..b47866a --- /dev/null +++ b/src/components/BannerSearch/BannerItem/BannerItem.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import styles from './BannerItem.module.scss' +import classNames from 'classnames/bind' +import { SearchBannerItem } from '../../../../types' +import { Link } from 'react-router-dom' + +const cx = classNames.bind(styles) + +const BannerItem: React.FC = ({ + title, + imageUrl, + bgColor, +}) => { + return ( + +
+ +

{title}

+
+ {title} +
+ +
+ + ) +} + +export default BannerItem diff --git a/src/components/BannerSearch/SearchBanner.module.scss b/src/components/BannerSearch/SearchBanner.module.scss new file mode 100644 index 0000000..d8af727 --- /dev/null +++ b/src/components/BannerSearch/SearchBanner.module.scss @@ -0,0 +1,19 @@ +.wrapper { + margin-top: 16px; + padding-inline: 24px; + + .heading { + font-size: 24px; + margin-top: 28px; + margin-bottom: 16px; + } + + .body { + display: grid; + gap: 24px; + grid-template-columns: repeat(5, 1fr); + } +} + + + diff --git a/src/components/BannerSearch/SearchBanner.tsx b/src/components/BannerSearch/SearchBanner.tsx new file mode 100644 index 0000000..4219994 --- /dev/null +++ b/src/components/BannerSearch/SearchBanner.tsx @@ -0,0 +1,57 @@ +import React, { useEffect, useState, useContext, useMemo } from 'react' +import styles from './SearchBanner.module.scss' +import classNames from 'classnames/bind' +import { SearchBannerItem } from './../../../types' +import BannerItem from './BannerItem/BannerItem' +import { MainLayoutContext } from '@/contexts/MainLayoutContext' + +const cx = classNames.bind(styles) + +const SearchBanner: React.FC = () => { + const [data, setData] = useState([]) + const [quantityCol, setQuantityCol] = useState(9) + + const { width } = useContext(MainLayoutContext) + + useEffect(() => { + if (width < 1850) setQuantityCol(8) + if (width < 1650) setQuantityCol(7) + if (width < 1450) setQuantityCol(6) + if (width < 1250) setQuantityCol(5) + if (width < 1000) setQuantityCol(4) + if (width < 700) setQuantityCol(3) + if (width < 500) setQuantityCol(2) + }, [width]) + + + useEffect(() => { + const fetchData = async () => { + const response = await fetch('/data/bannerSearch.json') + const data = await response.json() + setData(data) + } + + fetchData() + }, []) + + return ( +
+

Browse all

+
+ {data.map((item, index) => ( + + ))} +
+
+ ) +} + +export default SearchBanner diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss index 2365948..c84bfc3 100644 --- a/src/components/Footer/Footer.module.scss +++ b/src/components/Footer/Footer.module.scss @@ -5,10 +5,12 @@ display: flex; flex-direction: column; padding: 8px 32px 40px; + margin-top: 40px; &__top { display: flex; justify-content: space-between; + margin-top: 32px; &-links { display: flex; diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 897ecfc..5006b83 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -28,8 +28,8 @@ const Footer: React.FC = () => { ) } diff --git a/src/components/SearchBanner/SearchBanner.tsx b/src/components/SearchBanner/SearchBanner.tsx index e383db2..a96dab6 100644 --- a/src/components/SearchBanner/SearchBanner.tsx +++ b/src/components/SearchBanner/SearchBanner.tsx @@ -17,10 +17,10 @@ const SearchBanner: React.FC = () => { const data = await response.json() setData(data) } - fetchData() }, []) + return (

Browse all

diff --git a/src/components/SearchResult/SearchResult.tsx b/src/components/SearchResult/SearchResult.tsx index de4a432..c3d6014 100644 --- a/src/components/SearchResult/SearchResult.tsx +++ b/src/components/SearchResult/SearchResult.tsx @@ -104,9 +104,9 @@ const SearchResult: FC = ({ query }) => { return (
- {searchSelection.map((item, index) => ( + {searchSelection.map((item) => (
{ const [navPlayBtnVisible, setNavPlayBtnVisible] = useState(false) const [isLoading, setLoading] = useState(true) + useDocumentTitle( + `${overviewData?.profile?.name ? overviewData?.profile?.name : 'Artist'} | Spotify` + ) + const bannerRef = useRef() const { height: bannerHeight } = useComponentSize(bannerRef) @@ -33,7 +38,6 @@ const Artist: React.FC = () => { useEffect(() => { const fetchData = async () => { - // const data = await fetchArtistData(search.substring(1)) const data = await fetchArtistData(id) setOverViewData(data) } @@ -46,7 +50,6 @@ const Artist: React.FC = () => { const handleScroll = (e: React.UIEvent): void => { const yAxis = e.currentTarget.scrollTop - // console.log(yAxis) if (yAxis > bannerHeight / 2) { setNavOpacity(1) setBgBannerOpacity(1) @@ -66,29 +69,40 @@ const Artist: React.FC = () => { } else setNavPlayBtnVisible(false) } + console.log(overviewData) + return (
+ > + {!isLoading && ( + banner-image + )} +
+
handleScroll(e)} className={cx('body')}>
{
{ followerNumber={overviewData?.stats?.followers} monthlyListeners={overviewData?.stats.monthlyListeners} dominantColor={ + overviewData?.visuals?.avatarImage?.extractedColors?.colorRaw?.hex || overviewData?.visuals?.headerImage?.extractedColors?.colorRaw?.hex } bgBannerOpacity={bgBannerOpacity} diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 10c1221..5ca5f2f 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react' import { SectionProps } from '../../../types' import styles from './Home.module.scss' import { fetchHomePageSectionData } from '@/utils' +import { useDocumentTitle } from 'usehooks-ts' const cx = classNames.bind(styles) @@ -15,6 +16,8 @@ const Home: React.FC = () => { const [topMixes, setTopMixes] = useState() const [suggestArtists, setSuggestArtists] = useState() + useDocumentTitle('Spotify - Clone') + useEffect(() => { fetchHomePageSectionData({ type: 'newRelease', setData: setNewReleaseData }) fetchHomePageSectionData({ type: 'featuredPlaylists', setData: setFeaturePlaylist }) diff --git a/src/pages/Playlist/Playlist.tsx b/src/pages/Playlist/Playlist.tsx index b272b22..9b1d3c8 100644 --- a/src/pages/Playlist/Playlist.tsx +++ b/src/pages/Playlist/Playlist.tsx @@ -1,13 +1,14 @@ import { HeartIcon } from '@/assets/icons' import { Footer, Header, Navbar, SongList } from '@/components' import { PlayButton } from '@/components/UIs' -import { useRaiseColorTone } from '@/hooks' +import { useComponentSize, useRaiseColorTone } from '@/hooks' import useDominantColor from '@/hooks/useDominantColor' import { fetchSpotifyData, getAccessToken } from '@/utils/fetchData' import classNames from 'classnames/bind' -import React, { memo, useEffect, useState } from 'react' +import React, { memo, useEffect, useState, useRef } from 'react' import { useNavigate, useParams } from 'react-router-dom' import styles from './Playlist.module.scss' +import { useDocumentTitle } from 'usehooks-ts' const cx = classNames.bind(styles) @@ -15,9 +16,16 @@ const Playlist: React.FC = () => { const [navOpacity, setNavOpacity] = useState(0) const [data, setData] = useState() const [isLoading, setLoading] = useState(true) + const [navPlayBtnVisible, setNavPlayBtnVisible] = useState(false) + const bgColor = useRaiseColorTone(useDominantColor(data?.images[0].url) || '#121212') - - const {id} = useParams() + + useDocumentTitle(`${data?.name} | Spotify Playlist`) + + const headerRef = useRef() + const { height: headerHeight } = useComponentSize(headerRef) + + const { id } = useParams() const navigate = useNavigate() @@ -27,12 +35,13 @@ const Playlist: React.FC = () => { const data = await fetchSpotifyData({ type: 'playlists', accessToken: token, - // id: search.substring(1), id: id, }) if (data?.error) { navigate('/not-found') - } else setData({ ...data }) + } else { + setData({ ...data }) + } } if (id !== 'undefined') { fetchData() @@ -47,24 +56,35 @@ const Playlist: React.FC = () => { const yAxis = e.currentTarget.scrollTop if (yAxis > 64) { setNavOpacity(1) - return - } - setNavOpacity(yAxis / 64) + } else setNavOpacity(yAxis / 64) + if (yAxis > headerHeight + 14) { + setNavPlayBtnVisible(true) + } else setNavPlayBtnVisible(false) } + console.log(data) + return (
- +
handleScroll(e)} className={cx('body')}> -
+
+
+
diff --git a/src/pages/Search/Search.tsx b/src/pages/Search/Search.tsx index 36f1695..0d22211 100644 --- a/src/pages/Search/Search.tsx +++ b/src/pages/Search/Search.tsx @@ -2,6 +2,7 @@ import { Footer, Navbar, SearchBanner, SearchResult } from '@/components' import classNames from 'classnames/bind' import React, { FC, useEffect, useState } from 'react' import styles from './Search.module.scss' +import { useDocumentTitle } from 'usehooks-ts' const cx = classNames.bind(styles) @@ -13,6 +14,8 @@ const Search: FC = () => { const [query, setQuery] = useState('') const [debounceValue, setDebounceValue] = useState('') + useDocumentTitle('Spotify – Search') + useEffect(() => { let timeoutId: any if (!query.trim()) { @@ -28,7 +31,7 @@ const Search: FC = () => { return (
- +
{debounceValue && ( <> diff --git a/src/pages/Section/Section.tsx b/src/pages/Section/Section.tsx index f313771..9e039a7 100644 --- a/src/pages/Section/Section.tsx +++ b/src/pages/Section/Section.tsx @@ -11,12 +11,6 @@ import styles from './Section.module.scss' const cx = classNames.bind(styles) -// export interface SectionData { -// title?: string -// href?: string -// dataType?: string -// data?: ResponseSectionItem[] -// } const Section: React.FC = () => { const [data, setData] = useState() diff --git a/src/utils/fetchData.ts b/src/utils/fetchData.ts index cc1c35b..3af9c62 100644 --- a/src/utils/fetchData.ts +++ b/src/utils/fetchData.ts @@ -109,19 +109,17 @@ export const searchData = async (args: Partial) => { export const fetchArtistData = async (id: string | undefined) => { const apiKey = import.meta.env.VITE_RAPID_SPOTIFY_API - console.log(apiKey) const options = { method: 'GET', url: 'https://spotify23.p.rapidapi.com/artist_overview/', params: { - id: id + id: id, }, headers: { 'X-RapidAPI-Key': apiKey, - 'X-RapidAPI-Host': 'spotify23.p.rapidapi.com' - } - }; - const response = await axios.request(options); - return response.data.data.artist - + 'X-RapidAPI-Host': 'spotify23.p.rapidapi.com', + }, + } + const response = await axios.request(options) + return response.data.data.artist } diff --git a/src/utils/fetchHomePageSectionData.ts b/src/utils/fetchHomePageSectionData.ts index 0df0d9e..b507a33 100644 --- a/src/utils/fetchHomePageSectionData.ts +++ b/src/utils/fetchHomePageSectionData.ts @@ -23,7 +23,7 @@ const fetchHomePageSectionData = (args: Partial) => { if (data) { setData!({ title: 'New Releases', - href: '/section?newReleases', + href: '/section/newReleases', dataType: 'album', data: JSON.parse(data), apiType: 'spotify' @@ -39,7 +39,7 @@ const fetchHomePageSectionData = (args: Partial) => { localStorage.setItem('newReleasesData', JSON.stringify(responseData)) setData!({ title: 'New Releases', - href: '/section?newReleases', + href: '/section/newReleases', dataType: 'album', data: responseData, apiType: 'spotify' @@ -55,7 +55,7 @@ const fetchHomePageSectionData = (args: Partial) => { if (data) { setData!({ title: 'Feature Playlist', - href: '/section?featurePlaylist', + href: '/section/featurePlaylist', dataType: 'playlist', data: JSON.parse(data), apiType: 'spotify' @@ -71,7 +71,7 @@ const fetchHomePageSectionData = (args: Partial) => { localStorage.setItem('featuredPlaylists', JSON.stringify(responseData)) setData!({ title: 'Feature Playlist', - href: '/section?featurePlaylist', + href: '/section/featurePlaylist', dataType: 'playlist', data: responseData, apiType: 'spotify' @@ -87,7 +87,7 @@ const fetchHomePageSectionData = (args: Partial) => { if (data) { setData!({ title: 'Top mixes', - href: '/section?topMixes', + href: '/section/topMixes', dataType: 'playlist', data: JSON.parse(data), apiType: 'spotify' @@ -104,7 +104,7 @@ const fetchHomePageSectionData = (args: Partial) => { localStorage.setItem('topMixes', JSON.stringify(responseData?.playlists.items)) setData!({ title: 'Top mixes', - href: '/section?topMixes', + href: '/section/topMixes', dataType: 'playlist', data: responseData?.playlists.items, apiType: 'spotify' @@ -120,7 +120,7 @@ const fetchHomePageSectionData = (args: Partial) => { if (data) { setData!({ title: 'Suggested artists', - href: '/section?suggestedArtists', + href: '/section/suggestedArtists', dataType: 'artist', data: JSON.parse(data), apiType: 'spotify' @@ -144,7 +144,7 @@ const fetchHomePageSectionData = (args: Partial) => { ) setData!({ title: 'Suggested artists', - href: '/section?suggestedArtists', + href: '/section/suggestedArtists', dataType: 'artist', data: responseData?.artists.items .sort((a: any, b: any) => -a.popularity + b.popularity) diff --git a/src/utils/index.ts b/src/utils/index.ts index f026991..ecd5bbe 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,7 +2,7 @@ import htmlCleaner from './htmlCleaner' import fetchHomePageSectionData from './fetchHomePageSectionData' import dateFormatConvertor from './dateFormatConvertor' import stringCleaner from './stringCleaner' -import unicodeDecoder from './unicodeDecoder' +import { unicodeDecoder } from './unicodeDecoder' import fetchLocalData from './fetchLocalData' export { diff --git a/src/utils/unicodeDecoder.ts b/src/utils/unicodeDecoder.ts index 24976ab..4ef57df 100644 --- a/src/utils/unicodeDecoder.ts +++ b/src/utils/unicodeDecoder.ts @@ -1,4 +1,4 @@ -const unicodeDecoder = (input: string | undefined): string => { +export const unicodeDecoder = (input: string | undefined): string => { if(!input) return '' return input.replace( /&#(\d+);/g, @@ -7,5 +7,3 @@ const unicodeDecoder = (input: string | undefined): string => { } ) } - -export default unicodeDecoder \ No newline at end of file diff --git a/types.ts b/types.ts index a7e1866..3c99cf6 100644 --- a/types.ts +++ b/types.ts @@ -332,6 +332,7 @@ export interface SongItemProps { dateAdd?: string isExplicit?: boolean type?: 'default' | 'playlist' | 'album' | 'search' | 'artist' + id?: string } export interface SongListProps { songList: SongItemProps[] From d85e6844f72de368ce498ccc28be1e733066c660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tu=E1=BA=A5n=20=C4=90=E1=BA=B7ng?= <121254669+tuan204-dev@users.noreply.github.com> Date: Sun, 9 Jul 2023 10:25:18 +0000 Subject: [PATCH 48/99] test codespace github --- src/components/SongItemTag/SongItemTag.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SongItemTag/SongItemTag.tsx b/src/components/SongItemTag/SongItemTag.tsx index 2d1a599..cabc3ae 100644 --- a/src/components/SongItemTag/SongItemTag.tsx +++ b/src/components/SongItemTag/SongItemTag.tsx @@ -44,7 +44,7 @@ const SongItemTag: React.FC = (props) => {
- +
) : ( From 7c6ece7d0eace22ad63b2a3e3e575e886be133f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tu=E1=BA=A5n=20=C4=90=E1=BA=B7ng?= <121254669+tuan204-dev@users.noreply.github.com> Date: Sun, 9 Jul 2023 11:58:59 +0000 Subject: [PATCH 49/99] fix selection on Discograply and Search Result Component --- src/components/Discography/Discography.tsx | 34 ++++++++++---------- src/components/SearchBanner/SearchBanner.tsx | 1 - src/components/SearchResult/SearchResult.tsx | 18 ++++++++--- src/pages/Album/Album.tsx | 2 +- src/pages/Artist/Artist.tsx | 2 -- src/pages/Playlist/Playlist.tsx | 2 +- 6 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/components/Discography/Discography.tsx b/src/components/Discography/Discography.tsx index d1fe476..5119b85 100644 --- a/src/components/Discography/Discography.tsx +++ b/src/components/Discography/Discography.tsx @@ -18,47 +18,47 @@ const Discography: FC = ({ data }) => { key: 'popularReleases', display: 'Popular releases', active: 'popularReleases' === category, - isEmpty: data?.popularReleases.items.length === 0, + isExist: Boolean(data?.popularReleases?.totalCount), }, { key: 'albums', display: 'Albums', active: 'albums' === category, - isEmpty: data?.albums.items.length === 0, + isExist: Boolean(data?.albums?.totalCount), }, { key: 'singles', display: 'Singles', active: 'singles' === category, - isEmpty: data?.singles.items.length === 0, + isExist: Boolean(data?.singles?.totalCount), }, ], - [category] + [category, data] ) + return (

Discography

- {selection.map(item => { - if (!item.isEmpty) - return ( - - ) - })} + {selection + .filter((item) => item.isExist) + .map((item) => ( + + ))}
{ fetchData() }, []) - return (

Browse all

diff --git a/src/components/SearchResult/SearchResult.tsx b/src/components/SearchResult/SearchResult.tsx index c3d6014..ae169b2 100644 --- a/src/components/SearchResult/SearchResult.tsx +++ b/src/components/SearchResult/SearchResult.tsx @@ -16,47 +16,56 @@ const SearchResult: FC = ({ query }) => { const [data, setData] = useState() const [category, setCategory] = useState('all') - const searchSelection = useMemo( + const searchSelections = useMemo( () => [ { key: 'all', active: category === 'all', display: 'All', + isExist: true, }, { key: 'albums', active: category === 'albums', display: 'Albums', + isExist: Boolean(data?.albums?.total), }, { key: 'artists', active: category === 'artists', display: 'Artists', + isExist: Boolean(data?.artists?.total), }, { key: 'tracks', active: category === 'tracks', display: 'Songs', + isExist: Boolean(data?.tracks?.total), }, { key: 'playlists', active: category === 'playlists', display: 'Playlists', + isExist: Boolean(data?.playlists?.total), }, { key: 'shows', active: category === 'shows', display: 'Podcasts & Shows', + isExist: Boolean(data?.shows?.total), }, { key: 'episodes', active: category === 'episodes', display: 'Episodes', + isExist: Boolean(data?.episodes?.total), }, ], - [category] + [category, data] ) + console.log(data) + useEffect(() => { const fetchData = async () => { const token = await getAccessToken() @@ -74,7 +83,6 @@ const SearchResult: FC = ({ query }) => { if ( data?.albums.items.filter((item: any) => item).length === 0 && data?.artists.items.filter((item: any) => item).length === 0 && - data?.audiobooks.items.filter((item: any) => item).length === 0 && data?.episodes.items.filter((item: any) => item).length === 0 && data?.playlists.items.filter((item: any) => item).length === 0 && data?.shows.items.filter((item: any) => item).length === 0 && @@ -104,7 +112,7 @@ const SearchResult: FC = ({ query }) => { return (
- {searchSelection.map((item) => ( + {searchSelections.filter(item => item.isExist).map((item) => (
{category !== 'all' ? ( - searchSelection + searchSelections .filter((item) => item.active) .map((item) => { if (item.key !== 'tracks') { diff --git a/src/pages/Album/Album.tsx b/src/pages/Album/Album.tsx index e073232..f018953 100644 --- a/src/pages/Album/Album.tsx +++ b/src/pages/Album/Album.tsx @@ -19,7 +19,7 @@ const Album: React.FC = () => { const [isLoading, setLoading] = useState(true) const [navPlayBtnVisible, setNavPlayBtnVisible] = useState(false) - useDocumentTitle(`${data?.name} | Spotify`) + useDocumentTitle(`${data?.name ? data?.name : 'Album'} | Spotify`) const bgColor = useRaiseColorTone(useDominantColor(data?.images[0].url)) diff --git a/src/pages/Artist/Artist.tsx b/src/pages/Artist/Artist.tsx index a9f5def..490add4 100644 --- a/src/pages/Artist/Artist.tsx +++ b/src/pages/Artist/Artist.tsx @@ -69,7 +69,6 @@ const Artist: React.FC = () => { } else setNavPlayBtnVisible(false) } - console.log(overviewData) return (
@@ -87,7 +86,6 @@ const Artist: React.FC = () => {
{ const bgColor = useRaiseColorTone(useDominantColor(data?.images[0].url) || '#121212') - useDocumentTitle(`${data?.name} | Spotify Playlist`) + useDocumentTitle(`${data?.name ? data?.name : 'Playlist'} | Spotify Playlist`) const headerRef = useRef() const { height: headerHeight } = useComponentSize(headerRef) From 7ef875aafe40b4d4626b16199b04361ca615c571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tu=E1=BA=A5n=20=C4=90=E1=BA=B7ng?= <121254669+tuan204-dev@users.noreply.github.com> Date: Sun, 9 Jul 2023 13:49:01 +0000 Subject: [PATCH 50/99] merge --- .../SearchNotFound/SearchNotFound.module.scss | 33 ++++++++++++ .../SearchNotFound/SearchNotFound.tsx | 29 +++++++++++ .../SearchResult/SearchResult.module.scss | 23 --------- src/components/SearchResult/SearchResult.tsx | 51 +++++++------------ 4 files changed, 80 insertions(+), 56 deletions(-) create mode 100644 src/components/SearchResult/SearchNotFound/SearchNotFound.module.scss create mode 100644 src/components/SearchResult/SearchNotFound/SearchNotFound.tsx diff --git a/src/components/SearchResult/SearchNotFound/SearchNotFound.module.scss b/src/components/SearchResult/SearchNotFound/SearchNotFound.module.scss new file mode 100644 index 0000000..432f296 --- /dev/null +++ b/src/components/SearchResult/SearchNotFound/SearchNotFound.module.scss @@ -0,0 +1,33 @@ +@import '../../../scss/mixin'; + +.wrapper { + display: flex; + flex-direction: column; + min-height: 100vh; + justify-content: space-between; +} + + +.not-found { + min-height: 400px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding-inline: 24px; + overflow: hidden; + + .title { + display: block; + width: 100%; + font-size: 24px; + font-weight: 700; + text-align: center; + @include text-line-clamp(4); + + } + + .msg { + font-size: 14px; + } +} \ No newline at end of file diff --git a/src/components/SearchResult/SearchNotFound/SearchNotFound.tsx b/src/components/SearchResult/SearchNotFound/SearchNotFound.tsx new file mode 100644 index 0000000..f8d903d --- /dev/null +++ b/src/components/SearchResult/SearchNotFound/SearchNotFound.tsx @@ -0,0 +1,29 @@ +import {FC} from 'react' +import styles from './SearchNotFound.module.scss' +import classNames from 'classnames/bind' +import { Footer } from '@/components' + +const cx = classNames.bind(styles) + +interface SearchNotFoundProps { + query?: string +} + +const SearchNotFound: FC = ({query}) => { + return ( +
+
+

{`No results found for "${query}"`}

+ + Please make sure your words are spelled correctly, or use fewer or different + keywords. + +
+
+
+ ) +} + +export default SearchNotFound diff --git a/src/components/SearchResult/SearchResult.module.scss b/src/components/SearchResult/SearchResult.module.scss index c1cb4b9..8fbf292 100644 --- a/src/components/SearchResult/SearchResult.module.scss +++ b/src/components/SearchResult/SearchResult.module.scss @@ -40,29 +40,6 @@ } } -.not-found { - min-height: 400px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding-inline: 24px; - overflow: hidden; - - .title { - display: block; - width: 100%; - font-size: 24px; - font-weight: 700; - text-align: center; - @include text-line-clamp(4); - - } - - .msg { - font-size: 14px; - } -} .grid-md { grid-template-columns: 16px minmax(300px, 4fr) 2fr 50px !important; diff --git a/src/components/SearchResult/SearchResult.tsx b/src/components/SearchResult/SearchResult.tsx index ae169b2..0ac2bf7 100644 --- a/src/components/SearchResult/SearchResult.tsx +++ b/src/components/SearchResult/SearchResult.tsx @@ -1,10 +1,11 @@ -import { FC, useState, useEffect, useMemo } from 'react' -import styles from './SearchResult.module.scss' -import classNames from 'classnames/bind' -import TopResult from './TopResult/TopResult' -import { Footer, Section } from '..' import { getAccessToken, searchData } from '@/utils/fetchData' +import classNames from 'classnames/bind' +import { FC, useEffect, useMemo, useState } from 'react' +import { Section } from '..' import SongList from '../SongList/SongList' +import SearchNotFound from './SearchNotFound/SearchNotFound' +import styles from './SearchResult.module.scss' +import TopResult from './TopResult/TopResult' const cx = classNames.bind(styles) @@ -88,39 +89,23 @@ const SearchResult: FC = ({ query }) => { data?.shows.items.filter((item: any) => item).length === 0 && data?.tracks.items.filter((item: any) => item).length === 0 ) { - return ( -
-
-

{`No results found for "${query}"`}

- - Please make sure your words are spelled correctly, or use fewer or different - keywords. - -
-
-
- ) + return } return (
- {searchSelections.filter(item => item.isExist).map((item) => ( - - ))} + {searchSelections + .filter((item) => item.isExist) + .map((item) => ( + + ))}
{category !== 'all' ? ( From b8658c0193444f6ae0b16004af42a88ef364b1aa Mon Sep 17 00:00:00 2001 From: tuan204-dev Date: Mon, 10 Jul 2023 19:39:18 +0700 Subject: [PATCH 51/99] fixes some bug :v --- src/components/AboutArtist/AboutArtist.tsx | 4 +-- src/components/Header/Header.module.scss | 1 + src/components/Header/Header.tsx | 4 +-- src/components/Navbar/Navbar.tsx | 2 +- src/components/NotFound/NotFound.tsx | 3 ++ .../SearchResult/TopResult/TopResult.tsx | 6 ++-- src/components/SectionItem/SectionItem.tsx | 4 +-- src/components/SidebarItem/SidebarItem.tsx | 4 +-- src/components/SongItem/SongItem.tsx | 12 +++++--- src/components/SongList/SongList.tsx | 19 ++++++++----- src/components/TopTracks/TopTracks.tsx | 4 +-- src/components/UIs/PlayButton/PlayButton.tsx | 4 +-- .../SubTitle.module.scss} | 2 +- .../Artists.tsx => SubTitle/SubTitle.tsx} | 28 +++++++++++++------ src/components/UIs/index.ts | 4 +-- src/pages/Artist/Artist.tsx | 1 - types.ts | 5 +++- 17 files changed, 66 insertions(+), 41 deletions(-) rename src/components/UIs/{Artists/Artists.module.scss => SubTitle/SubTitle.module.scss} (99%) rename src/components/UIs/{Artists/Artists.tsx => SubTitle/SubTitle.tsx} (62%) diff --git a/src/components/AboutArtist/AboutArtist.tsx b/src/components/AboutArtist/AboutArtist.tsx index 9995923..f6f035e 100644 --- a/src/components/AboutArtist/AboutArtist.tsx +++ b/src/components/AboutArtist/AboutArtist.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, memo } from 'react' import styles from './AboutArtist.module.scss' import classNames from 'classnames/bind' import { unicodeDecoder } from '@/utils' @@ -39,4 +39,4 @@ const AboutArtist: FC = ({ profile, stats, visuals, isLoading ) } -export default AboutArtist +export default memo(AboutArtist) diff --git a/src/components/Header/Header.module.scss b/src/components/Header/Header.module.scss index 0ac6838..4a84c58 100644 --- a/src/components/Header/Header.module.scss +++ b/src/components/Header/Header.module.scss @@ -96,6 +96,7 @@ font-size: 14px; color: $white; font-weight: 700; + max-width: 40%; } .release-date {} diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 67afae3..a8eff4f 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -5,7 +5,7 @@ import React, { memo } from 'react' import Skeleton from 'react-loading-skeleton' import { ArtistList } from '..' import styles from './Header.module.scss' -import { Artists } from '../UIs' +import { SubTitle } from '../UIs' import { HeaderProps } from '../../../types' import { LazyLoadImage } from 'react-lazy-load-image-component' @@ -57,7 +57,7 @@ const Header: React.FC = ({ {(type === 'album' || type === 'single' || type === 'compilation') && ( <>
- {} + {}
{' '}
{' '}
{releaseDate?.slice(0, 4)}
diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 3638033..1524101 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -70,7 +70,7 @@ const Navbar: FC = (props) => { {type === 'search' && (
- + e.preventDefault()}> { + useDocumentTitle('Page not found') return (
= ({ topResult, songs }) => {
{!isLoading ? ( - + ) : ( )} @@ -88,7 +88,7 @@ const TopResult: FC = ({ topResult, songs }) => { thumb={item.album.images[item.album.images.length - 1].url} duration={item.duration_ms} order={index + 1} - album={item.album.name} + albumData={{name: item.album.name}} isExplicit={item.explicit} type='search' /> diff --git a/src/components/SectionItem/SectionItem.tsx b/src/components/SectionItem/SectionItem.tsx index 8999479..428bfa6 100644 --- a/src/components/SectionItem/SectionItem.tsx +++ b/src/components/SectionItem/SectionItem.tsx @@ -7,7 +7,7 @@ import Skeleton from 'react-loading-skeleton' import 'react-loading-skeleton/dist/skeleton.css' import { Link } from 'react-router-dom' import { SectionItemI } from '../../../types' -import { Artists, PlayButton } from '../UIs' +import { SubTitle, PlayButton } from '../UIs' import styles from './SectionItem.module.scss' const cx = classNames.bind(styles) @@ -78,7 +78,7 @@ const SectionItem: React.FC = ({ desc || (author && `By ${author}`) || (dataType === 'artist' && 'Artist') || - (artists && ) || + (artists && ) || 'Lorem ipsum dolor sit amet consectetur adipisicing elit.'}

) : ( diff --git a/src/components/SidebarItem/SidebarItem.tsx b/src/components/SidebarItem/SidebarItem.tsx index b5cecd7..a9e4d7a 100644 --- a/src/components/SidebarItem/SidebarItem.tsx +++ b/src/components/SidebarItem/SidebarItem.tsx @@ -2,7 +2,7 @@ import classNames from 'classnames/bind' import { FC } from 'react' import styles from './SidebarItem.module.scss' import { Link } from 'react-router-dom' -import { Artists } from '../UIs' +import { SubTitle } from '../UIs' import { LazyLoadImage } from 'react-lazy-load-image-component' const cx = classNames.bind(styles) @@ -22,7 +22,7 @@ const SidebarItem: FC = (props) => { const newType = (() => { if(type === 'playlist') return author if(type === 'artist') return 'Artist' - if(type === 'album' && artists) return + if(type === 'album' && artists) return })() return ( diff --git a/src/components/SongItem/SongItem.tsx b/src/components/SongItem/SongItem.tsx index 5f79f2c..4041a30 100644 --- a/src/components/SongItem/SongItem.tsx +++ b/src/components/SongItem/SongItem.tsx @@ -3,7 +3,7 @@ import { dateFormatConvertor } from '@/utils' import classNames from 'classnames/bind' import React, { memo, useContext } from 'react' import Skeleton from 'react-loading-skeleton' -import { Artists } from '../UIs' +import { SubTitle } from '../UIs' import styles from './SongItem.module.scss' import { PlayIcon } from '@/assets/icons' import { SongItemProps } from '../../../types' @@ -20,7 +20,7 @@ const SongItem: React.FC = ({ order, isLoading = false, dateAdd, - album, + albumData, isExplicit = false, type = 'default', }) => { @@ -64,7 +64,7 @@ const SongItem: React.FC = ({ {type !== 'artist' && (
{isExplicit && E} - +
)} @@ -78,7 +78,11 @@ const SongItem: React.FC = ({
{type !== 'album' && type !== 'search' && ( <> -
{!isLoading && album}
+
+ {!isLoading && ( + + )} +
{width > 780 && (
{dateAdd !== '1970-01-01T00:00:00Z' && dateAdd diff --git a/src/components/SongList/SongList.tsx b/src/components/SongList/SongList.tsx index 89156ba..5cc1d1c 100644 --- a/src/components/SongList/SongList.tsx +++ b/src/components/SongList/SongList.tsx @@ -1,4 +1,5 @@ -import { FC, useContext } from 'react' +/* eslint-disable react-refresh/only-export-components */ +import { FC, memo, useContext } from 'react' import styles from './SongList.module.scss' import classNames from 'classnames/bind' import SongItem from '../SongItem/SongItem' @@ -14,7 +15,7 @@ const SongList: FC = ({ pivotTop, isLoading = false, top, - type = 'default' + type = 'default', }) => { const { width } = useContext(MainLayoutContext) @@ -22,7 +23,6 @@ const SongList: FC = ({ threshold: 0, }) - return (
= ({ return songList?.map((item: any, index: number) => ( = ({ } songName={item?.name || item?.track?.name} artists={item?.artists || item?.track?.artists} - album={item?.album?.name || item?.track?.album?.name} + albumData={{ + name: item?.album?.name || item?.track?.album?.name, + id: item?.track?.album?.id, + }} dateAdd={item?.added_at} duration={item?.duration_ms || item?.track?.duration_ms} isExplicit={item?.explicit || item?.track?.explicit} @@ -87,7 +90,9 @@ const SongList: FC = ({ } else { return Array(9) ?.fill(0) - ?.map((item, index) => ) + ?.map((item, index) => ( + + )) } })()}
@@ -95,4 +100,4 @@ const SongList: FC = ({ ) } -export default SongList +export default memo(SongList) diff --git a/src/components/TopTracks/TopTracks.tsx b/src/components/TopTracks/TopTracks.tsx index e688909..0c1b1e2 100644 --- a/src/components/TopTracks/TopTracks.tsx +++ b/src/components/TopTracks/TopTracks.tsx @@ -47,11 +47,11 @@ const TopTrack: FC = ({ songList, isLoading }) => { ))}
-
+ {songList?.length > 5 &&
-
+
}
) } diff --git a/src/components/UIs/PlayButton/PlayButton.tsx b/src/components/UIs/PlayButton/PlayButton.tsx index ff5c8e5..f9dbbd6 100644 --- a/src/components/UIs/PlayButton/PlayButton.tsx +++ b/src/components/UIs/PlayButton/PlayButton.tsx @@ -1,4 +1,4 @@ -import { FC, useState } from 'react' +import { FC, useState, memo } from 'react' import styles from './PlayButton.module.scss' import classNames from 'classnames/bind' import { TbPlayerPlayFilled } from 'react-icons/tb' @@ -42,4 +42,4 @@ const PlayButton: FC = (props) => { ) } -export default PlayButton +export default memo(PlayButton) diff --git a/src/components/UIs/Artists/Artists.module.scss b/src/components/UIs/SubTitle/SubTitle.module.scss similarity index 99% rename from src/components/UIs/Artists/Artists.module.scss rename to src/components/UIs/SubTitle/SubTitle.module.scss index 9069c7c..92d4c78 100644 --- a/src/components/UIs/Artists/Artists.module.scss +++ b/src/components/UIs/SubTitle/SubTitle.module.scss @@ -8,7 +8,7 @@ gap: 3px; overflow: hidden; @include text-in-one-line; - + .artist-item { font-size: 14px; color: $text-neutral; diff --git a/src/components/UIs/Artists/Artists.tsx b/src/components/UIs/SubTitle/SubTitle.tsx similarity index 62% rename from src/components/UIs/Artists/Artists.tsx rename to src/components/UIs/SubTitle/SubTitle.tsx index db86773..d5b0c6c 100644 --- a/src/components/UIs/Artists/Artists.tsx +++ b/src/components/UIs/SubTitle/SubTitle.tsx @@ -1,6 +1,6 @@ import { Fragment, FC } from 'react' import { Link } from 'react-router-dom' -import styles from './Artists.module.scss' +import styles from './SubTitle.module.scss' import classNames from 'classnames/bind' const cx = classNames.bind(styles) @@ -8,17 +8,19 @@ const cx = classNames.bind(styles) interface ArtistsProps { data: any isWhiteColor?: boolean + type?: 'artist' | 'album' } -const Artists: FC = ({ data, isWhiteColor }) => { +const Artists: FC = ({ data, isWhiteColor, type = 'artist' }) => { const renderData: any[] = [] if (data) { if (data.length === 1) { renderData.push( - - + + {data[0].name} @@ -27,7 +29,9 @@ const Artists: FC = ({ data, isWhiteColor }) => { for (let i = 0; i < data.length - 1; i++) { renderData.push( - + = ({ data, isWhiteColor }) => { renderData.push( - + = ({ data, isWhiteColor }) => { } } - return <>{renderData} + return
{renderData}
} export default Artists diff --git a/src/components/UIs/index.ts b/src/components/UIs/index.ts index d033da3..bf3966d 100644 --- a/src/components/UIs/index.ts +++ b/src/components/UIs/index.ts @@ -1,7 +1,7 @@ -import Artists from "./Artists/Artists"; +import SubTitle from "./SubTitle/SubTitle"; import PlayButton from "./PlayButton/PlayButton"; export { - Artists, + SubTitle, PlayButton } \ No newline at end of file diff --git a/src/pages/Artist/Artist.tsx b/src/pages/Artist/Artist.tsx index 490add4..78059f9 100644 --- a/src/pages/Artist/Artist.tsx +++ b/src/pages/Artist/Artist.tsx @@ -69,7 +69,6 @@ const Artist: React.FC = () => { } else setNavPlayBtnVisible(false) } - return (
Date: Tue, 11 Jul 2023 02:15:00 +0700 Subject: [PATCH 52/99] create ArtistContext --- src/App.tsx | 11 +- src/components/AboutArtist/AboutArtist.tsx | 7 +- src/components/ArtistBanner/ArtistBanner.tsx | 18 +-- src/components/Discography/Discography.tsx | 12 +- .../Header/ArtistList/ArtistList.module.scss | 2 + .../Header/ArtistList/ArtistList.tsx | 2 +- src/components/SongItem/SongItem.module.scss | 1 + src/components/TopTracks/TopTracks.tsx | 14 ++- .../UIs/SubTitle/SubTitle.module.scss | 1 + src/contexts/ArtistContext.tsx | 108 ++++++++++++++++ src/layouts/RootLayout/RootLayout.tsx | 35 +++--- src/pages/Artist/Artist.tsx | 119 ++++++++---------- 12 files changed, 219 insertions(+), 111 deletions(-) create mode 100644 src/contexts/ArtistContext.tsx diff --git a/src/App.tsx b/src/App.tsx index 00f454a..9d0a916 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,15 @@ import { useEffect } from 'react' import { Route, Routes } from 'react-router-dom' +import { NotFound } from './components' import RootLayout from './layouts/RootLayout/RootLayout' +import Album from './pages/Album/Album' +import Artist from './pages/Artist/Artist' +import Episode from './pages/Episode/Episode' import Home from './pages/Home/Home' +import Playlist from './pages/Playlist/Playlist' import Search from './pages/Search/Search' import Section from './pages/Section/Section' -import Artist from './pages/Artist/Artist' -import Playlist from './pages/Playlist/Playlist' -import Album from './pages/Album/Album' import Show from './pages/Show/Show' -import Episode from './pages/Episode/Episode' -import { NotFound } from './components' const App = () => { useEffect(() => { @@ -18,6 +18,7 @@ const App = () => { return () => window.removeEventListener('beforeunload', clearLocalStorage) }, []) + return ( }> diff --git a/src/components/AboutArtist/AboutArtist.tsx b/src/components/AboutArtist/AboutArtist.tsx index f6f035e..ff1253d 100644 --- a/src/components/AboutArtist/AboutArtist.tsx +++ b/src/components/AboutArtist/AboutArtist.tsx @@ -10,10 +10,11 @@ interface AboutArtistProps { profile?: any visuals?: any isLoading?: boolean + aboutImg?: string } -const AboutArtist: FC = ({ profile, stats, visuals, isLoading }) => { - const desc = unicodeDecoder(profile?.biography.text) +const AboutArtist: FC = ({ profile, stats, isLoading, aboutImg }) => { + const desc = unicodeDecoder(profile?.bio) return (
@@ -22,7 +23,7 @@ const AboutArtist: FC = ({ profile, stats, visuals, isLoading
{!isLoading && ( diff --git a/src/components/ArtistBanner/ArtistBanner.tsx b/src/components/ArtistBanner/ArtistBanner.tsx index be85a1a..c028ed7 100644 --- a/src/components/ArtistBanner/ArtistBanner.tsx +++ b/src/components/ArtistBanner/ArtistBanner.tsx @@ -6,14 +6,14 @@ import styles from './ArtistBanner.module.scss' const cx = classNames.bind(styles) interface ArtistBannerProps { - name: string - avatar: string + name?: string + avatar?: string followerNumber?: number dominantColor?: string - monthlyListeners: number + monthlyListeners?: number bgBannerOpacity?: number isVerified?: boolean - isHeaderImg?: boolean + inclHeaderImg?: boolean isLoading?: boolean } @@ -24,16 +24,18 @@ const ArtistBanner: FC = (props) => { dominantColor, bgBannerOpacity, isVerified, - isHeaderImg, + inclHeaderImg, avatar, isLoading, } = props + console.log(isLoading) + return ( -
+
{!isLoading && (
- {!isHeaderImg && ( + {!inclHeaderImg && (
avt
@@ -61,7 +63,7 @@ const ArtistBanner: FC = (props) => { className={cx('blur')} style={{ backgroundColor: dominantColor, - opacity: isHeaderImg ? bgBannerOpacity : 1, + opacity: inclHeaderImg ? bgBannerOpacity : 1, }} >
diff --git a/src/components/Discography/Discography.tsx b/src/components/Discography/Discography.tsx index 5119b85..2869079 100644 --- a/src/components/Discography/Discography.tsx +++ b/src/components/Discography/Discography.tsx @@ -18,20 +18,20 @@ const Discography: FC = ({ data }) => { key: 'popularReleases', display: 'Popular releases', active: 'popularReleases' === category, - isExist: Boolean(data?.popularReleases?.totalCount), + isExist: Boolean(data?.popularReleases?.length), }, { key: 'albums', display: 'Albums', active: 'albums' === category, - isExist: Boolean(data?.albums?.totalCount), + isExist: Boolean(data?.albums?.length), }, { key: 'singles', display: 'Singles', active: 'singles' === category, - isExist: Boolean(data?.singles?.totalCount), + isExist: Boolean(data?.singles?.length), }, ], [category, data] @@ -61,9 +61,9 @@ const Discography: FC = ({ data }) => { apiType="rapid" dataType="album" data={ - (category === 'popularReleases' && data?.popularReleases.items) || - (category === 'albums' && data?.albums.items) || - (category === 'singles' && data?.singles.items) + (category === 'popularReleases' && data?.popularReleases) || + (category === 'albums' && data?.albums) || + (category === 'singles' && data?.singles) } type="artist" hideHeader diff --git a/src/components/Header/ArtistList/ArtistList.module.scss b/src/components/Header/ArtistList/ArtistList.module.scss index 3fef7b8..201712c 100644 --- a/src/components/Header/ArtistList/ArtistList.module.scss +++ b/src/components/Header/ArtistList/ArtistList.module.scss @@ -1,8 +1,10 @@ @import '../../../scss/variable'; +@import '../../../scss/mixin'; .wrapper { display: flex; gap: 3px; + @include text-line-clamp(2); .name { color: $text-neutral; diff --git a/src/components/Header/ArtistList/ArtistList.tsx b/src/components/Header/ArtistList/ArtistList.tsx index 7556bb8..5e68b19 100644 --- a/src/components/Header/ArtistList/ArtistList.tsx +++ b/src/components/Header/ArtistList/ArtistList.tsx @@ -13,7 +13,7 @@ interface ArtistListProps { const ArtistList: React.FC = (props) => { const { data } = props if (typeof data === 'string') { - return
{data}
+ return
{data}
} const renderData: any = [] if (data) { diff --git a/src/components/SongItem/SongItem.module.scss b/src/components/SongItem/SongItem.module.scss index 06eba07..bcb9c44 100644 --- a/src/components/SongItem/SongItem.module.scss +++ b/src/components/SongItem/SongItem.module.scss @@ -77,6 +77,7 @@ display: flex; align-items: center; gap: 3px; + width: 100%; @include text-in-one-line; } diff --git a/src/components/TopTracks/TopTracks.tsx b/src/components/TopTracks/TopTracks.tsx index 0c1b1e2..9670973 100644 --- a/src/components/TopTracks/TopTracks.tsx +++ b/src/components/TopTracks/TopTracks.tsx @@ -7,7 +7,7 @@ import styles from './TopTracks.module.scss' const cx = classNames.bind(styles) interface TopTrackProps { - songList: { + songList?: { track: SongItemProps }[] isLoading?: boolean @@ -47,11 +47,13 @@ const TopTrack: FC = ({ songList, isLoading }) => { ))}
- {songList?.length > 5 &&
- -
} + {songList?.length && songList?.length > 5 && ( +
+ +
+ )}
) } diff --git a/src/components/UIs/SubTitle/SubTitle.module.scss b/src/components/UIs/SubTitle/SubTitle.module.scss index 92d4c78..b9077e2 100644 --- a/src/components/UIs/SubTitle/SubTitle.module.scss +++ b/src/components/UIs/SubTitle/SubTitle.module.scss @@ -7,6 +7,7 @@ display: flex; gap: 3px; overflow: hidden; + width: 100%; @include text-in-one-line; .artist-item { diff --git a/src/contexts/ArtistContext.tsx b/src/contexts/ArtistContext.tsx new file mode 100644 index 0000000..294adc1 --- /dev/null +++ b/src/contexts/ArtistContext.tsx @@ -0,0 +1,108 @@ +import { fetchArtistData } from '@/utils/fetchData' +import { FC, ReactNode, createContext, useEffect, useState } from 'react' +import { useLocation } from 'react-router-dom' + +interface ArtistProviderProps { + children: ReactNode +} + +interface ArtistContext { + profile: { + id: string | undefined + name: string | undefined + bio: string | undefined + isVerified: boolean | undefined + } + avatarImg: string | undefined + headerImg: string | undefined + colorRaw: string | undefined + stats: { + followerNumbers: number | undefined + monthlyListeners: number | undefined + } + topTracks: any[] | undefined + discography: { + popularReleases: any[] | undefined + albums: any[] | undefined + singles: any[] | undefined + } + playlists: any[] | undefined + featuring: any[] | undefined + relatedArtists: any[] | undefined + discoveredOn: any[] | undefined + aboutImg: string + setId: React.Dispatch> + isLoading: boolean + visuals: any +} + +export const ArtistContext = createContext({} as ArtistContext) + +export const ArtistProvider: FC = ({ children }) => { + const [id, setId] = useState('') + const [responseData, setResponseData] = useState() + const [artistData, setArtistData] = useState() + const [isLoading, setLoading] = useState(true) + const regex = /^\/artist\//; + + const {pathname} = useLocation() + + useEffect(() => { + if(!regex.test(pathname)) setArtistData(undefined) + }, [id, pathname]) + + useEffect(() => { + const fetchData = async () => { + const data = await fetchArtistData(id) + setResponseData(data) + } + if (id !== '') fetchData() + }, [id]) + + useEffect(() => { + setArtistData((prev: any) => { + return { + ...prev, + profile: { + id: responseData?.id, + name: responseData?.profile?.name, + bio: responseData?.profile?.biography?.text, + isVerified: responseData?.profile?.verified, + }, + headerImg: responseData?.visuals?.headerImage?.sources[0]?.url, + avatarImg: responseData?.visuals?.avatarImage?.sources[0]?.url, + colorRaw: + responseData?.visuals?.headerImage?.extractedColors?.colorRaw || + responseData?.visuals?.avatarImage?.extractedColors?.colorRaw?.hex, + stats: { + followerNumbers: responseData?.stats?.followers, + monthlyListeners: responseData?.stats?.monthlyListeners, + }, + topTracks: responseData?.discography?.topTracks?.items, + discography: { + popularReleases: responseData?.discography?.popularReleases.items, + albums: responseData?.discography?.albums.items, + singles: responseData?.discography?.singles.items, + }, + playlists: responseData?.profile?.playlists?.items, + featuring: responseData?.relatedContent?.featuring?.items, + relatedArtists: responseData?.relatedContent?.relatedArtists?.items, + discoveredOn: responseData?.relatedContent?.featuring?.items, + aboutImg: responseData?.visuals?.gallery?.items[0]?.sources[0]?.url, + visuals: responseData?.visuals, + } + }) + }, [responseData]) + + useEffect(() => { + setLoading(Boolean(!responseData)) + }, [responseData]) + + console.log('rerender') + + return ( + + {children} + + ) +} diff --git a/src/layouts/RootLayout/RootLayout.tsx b/src/layouts/RootLayout/RootLayout.tsx index 935a738..dd4c225 100644 --- a/src/layouts/RootLayout/RootLayout.tsx +++ b/src/layouts/RootLayout/RootLayout.tsx @@ -6,28 +6,31 @@ import { SkeletonTheme } from 'react-loading-skeleton' import Split from 'react-split' import { Sidebar } from '@/components' import { MainLayoutProvider } from '@/contexts/MainLayoutContext' +import { ArtistProvider } from '@/contexts/ArtistContext' const cx = classNames.bind(styles) const RootLayout: FC = () => { return (
- - - - -
- -
-
-
-
+ + + + + +
+ +
+
+
+
+
) } diff --git a/src/pages/Artist/Artist.tsx b/src/pages/Artist/Artist.tsx index 78059f9..97d9cc2 100644 --- a/src/pages/Artist/Artist.tsx +++ b/src/pages/Artist/Artist.tsx @@ -8,45 +8,49 @@ import { } from '@/components' import ArtistBanner from '@/components/ArtistBanner/ArtistBanner' import { PlayButton } from '@/components/UIs' +import { ArtistContext } from '@/contexts/ArtistContext' import { useComponentSize } from '@/hooks' -import { fetchArtistData } from '@/utils/fetchData' import classNames from 'classnames/bind' -import React, { useEffect, useRef, useState } from 'react' +import React, { useContext, useEffect, useRef, useState } from 'react' import { useParams } from 'react-router-dom' -import styles from './Artist.module.scss' import { useDocumentTitle } from 'usehooks-ts' +import styles from './Artist.module.scss' const cx = classNames.bind(styles) const Artist: React.FC = () => { - const [overviewData, setOverViewData] = useState() const [navOpacity, setNavOpacity] = useState(0) const [bgBannerOpacity, setBgBannerOpacity] = useState(0) const [bgBannerScale, setBgBannerScale] = useState(1.05) const [navPlayBtnVisible, setNavPlayBtnVisible] = useState(false) - const [isLoading, setLoading] = useState(true) - - useDocumentTitle( - `${overviewData?.profile?.name ? overviewData?.profile?.name : 'Artist'} | Spotify` - ) - - const bannerRef = useRef() - - const { height: bannerHeight } = useComponentSize(bannerRef) const { id } = useParams() + const { + setId, + isLoading, + profile, + headerImg, + avatarImg, + colorRaw, + stats, + topTracks, + discography, + playlists, + featuring, + relatedArtists, + discoveredOn, + aboutImg, + visuals, + } = useContext(ArtistContext) + useDocumentTitle(`${profile?.name ? profile?.name : 'Artist'} | Spotify`) useEffect(() => { - const fetchData = async () => { - const data = await fetchArtistData(id) - setOverViewData(data) - } - fetchData() + setId(id) }, [id]) - useEffect(() => { - setLoading(Boolean(!overviewData)) - }, [overviewData]) + const bannerRef = useRef() + + const { height: bannerHeight } = useComponentSize(bannerRef) const handleScroll = (e: React.UIEvent): void => { const yAxis = e.currentTarget.scrollTop @@ -74,30 +78,20 @@ const Artist: React.FC = () => {
- {!isLoading && ( - banner-image - )} + {!isLoading && banner-image}
handleScroll(e)} className={cx('body')}> @@ -115,26 +109,21 @@ const Artist: React.FC = () => { >
@@ -142,56 +131,54 @@ const Artist: React.FC = () => {
- - + + - {overviewData?.relatedContent?.featuring?.items.length !== 0 && ( + {featuring?.length !== 0 && (
)} - {overviewData?.relatedContent?.relatedArtists?.items.length !== 0 && ( + {relatedArtists?.length !== 0 && (
)} - {overviewData?.profile?.playlists?.items?.length !== 0 && ( + {playlists?.length !== 0 && (
)} - {overviewData?.relatedContent?.discoveredOn?.items?.length !== 0 && ( + {discoveredOn?.length !== 0 && (
)} - {overviewData?.visuals?.gallery?.items[0]?.sources[0]?.url && ( + {aboutImg && ( )}