diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index fcfe926..db8cf8c 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -15,6 +15,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + pull-requests: write security-events: write actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status steps: @@ -25,7 +26,7 @@ jobs: run: yarn add @biomejs/biome - name: Run lint - run: yarn lint + run: yarn ci continue-on-error: false analyze: diff --git a/.vscode/settings.json b/.vscode/settings.json index 910058e..b68f6d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,7 +15,7 @@ }, "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "quickfix.biome": true, - "source.organizeImports.biome": true + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" } } diff --git a/biome.json b/biome.json index 8e2f1d1..62fc7fc 100644 --- a/biome.json +++ b/biome.json @@ -21,7 +21,7 @@ "enabled": true, "formatWithErrors": true, "indentStyle": "space", - "indentSize": 2, + "indentWidth": 2, "lineWidth": 80, "ignore": [] }, diff --git a/package.json b/package.json index da3c7f8..93fe699 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "version": "0.1.0", "private": true, "dependencies": { - "@biomejs/biome": "^1.1.2", + "@biomejs/biome": "^1.6.0", "@reduxjs/toolkit": "^1.9.5", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", "axios": "^1.4.0", - "fast-average-color": "^9.3.0", + "fast-average-color": "^9.4.0", "qs": "^6.11.2", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -24,7 +24,9 @@ "build": "tsc && vite build", "serve": "vite preview", "lint": "yarn biome check src/", - "format": "yarn biome format src/ --write" + "lint:fix": "yarn biome check src/ --apply", + "format": "yarn biome format src/ --write", + "ci": "yarn biome ci src/" }, "browserslist": { "production": [ diff --git a/src/API.ts b/src/API.ts index f8e0bea..00ed3b0 100644 --- a/src/API.ts +++ b/src/API.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import qs from 'qs'; import Cookies from 'universal-cookie'; -import { PlaylistType } from './types/playlist.interface'; -import { FeaturedPlaylistsResponse } from './types/playlists.interface'; +import type { PlaylistType } from './types/playlist.interface'; +import type { FeaturedPlaylistsResponse } from './types/playlists.interface'; const BASE_URL = 'https://api.spotify.com/v1'; const cookies = new Cookies(); diff --git a/src/App.tsx b/src/App.tsx index ec8464a..1dd1b33 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,7 +22,7 @@ const App = () => { useEffect(() => { void dispatch(fetchFeaturedPlaylists()); - }, []); + }, [dispatch]); return ( <> diff --git a/src/components/Player/Player.tsx b/src/components/Player/Player.tsx index 6c2ef1b..6ef95dd 100644 --- a/src/components/Player/Player.tsx +++ b/src/components/Player/Player.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { type ElementRef, useEffect, useRef, useState } from 'react'; import { ReactComponent as Like } from '../../assets/like.svg'; import { ReactComponent as Pause } from '../../assets/pause.svg'; import { ReactComponent as Play } from '../../assets/play.svg'; @@ -17,19 +17,20 @@ import styles from './Player.module.scss'; const Player = () => { const dispatch = useAppDispatch(); const { song, playing } = useAppSelector(selectCurrentSong); - const audioEml = useRef(null); + const audioEml = useRef>(null); - const timeRef = useRef(null); + const timeRef = useRef>(null); const barCallBack = useBar; const [progress, setProgress] = useState(0); const [currentTime, resetTime, toggleStopwatch] = useStopwatch(); const [volume, setVolume] = useState(70); - const volumeRef = useRef(null); + const volumeRef = useRef>(null); const [mute, setMute] = useState(false); // If the songs changes, plays it + // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect(() => { audioEml.current?.play().catch(() => { console.log('Unable to play'); diff --git a/src/components/SideBar/SideBar.tsx b/src/components/SideBar/SideBar.tsx index 5440716..c9e0b25 100644 --- a/src/components/SideBar/SideBar.tsx +++ b/src/components/SideBar/SideBar.tsx @@ -1,7 +1,7 @@ import { Link } from 'react-router-dom'; import { ReactComponent as Logo } from '../../assets/logo.svg'; -import { PlaylistType } from '../../types/playlist.interface'; -import { PlaylistsType } from '../../types/playlists.interface'; +import type { PlaylistType } from '../../types/playlist.interface'; +import type { PlaylistsType } from '../../types/playlists.interface'; import styles from './SideBar.module.scss'; const SideBar = ({ playlists }: { playlists: PlaylistsType }) => ( diff --git a/src/pages/PlaylistDetail/PlaylistDetail.tsx b/src/pages/PlaylistDetail/PlaylistDetail.tsx index cc2ee10..0b73b63 100644 --- a/src/pages/PlaylistDetail/PlaylistDetail.tsx +++ b/src/pages/PlaylistDetail/PlaylistDetail.tsx @@ -13,7 +13,7 @@ import { fetchPlaylistById, playlistDetailsSelector, } from '../../store/reducers/playlistDetail.slice'; -import { Track } from '../../types/track.interface'; +import type { Track } from '../../types/track.interface'; import msToMinutesAndSeconds from '../../utils/msToMinutes'; import styles from './PlaylistDetail.module.scss'; import SongItem from './SongItem/SongItem'; @@ -29,7 +29,7 @@ const PlaylistDetail = () => { if (id != null) { void dispatch(fetchPlaylistById(id)); } - }, [id]); + }, [id, dispatch]); useEffect(() => { if (coverRef.current != null) { @@ -51,7 +51,7 @@ const PlaylistDetail = () => { console.log(err); }); } - }, [playlist]); + }, []); const songClicked = (clickedSong: Track): void => { if (clickedSong.track.preview_url !== '') { @@ -63,9 +63,9 @@ const PlaylistDetail = () => { let totalMS = 0; if (playlist != null) { const { items } = playlist.tracks; - items.forEach(({ track }) => { - totalMS += track.duration_ms; - }); + for (const item of items) { + totalMS += item.track.duration_ms; + } return `about ${msToMinutesAndSeconds(totalMS)}`; } return 'could not load duration'; diff --git a/src/pages/PlaylistDetail/SongItem/SongItem.tsx b/src/pages/PlaylistDetail/SongItem/SongItem.tsx index d0f93c5..0dea171 100644 --- a/src/pages/PlaylistDetail/SongItem/SongItem.tsx +++ b/src/pages/PlaylistDetail/SongItem/SongItem.tsx @@ -1,5 +1,5 @@ import { ReactComponent as Play } from '../../../assets/play.svg'; -import { Track } from '../../../types/track.interface'; +import type { Track } from '../../../types/track.interface'; import formatDate from '../../../utils/formatDate'; import msToMinutesAndSeconds from '../../../utils/msToMinutes'; import styles from './SongItem.module.scss'; diff --git a/src/pages/Playlists/PlaylistItem/PlaylistItem.tsx b/src/pages/Playlists/PlaylistItem/PlaylistItem.tsx index d8ca4f4..dc119cd 100644 --- a/src/pages/Playlists/PlaylistItem/PlaylistItem.tsx +++ b/src/pages/Playlists/PlaylistItem/PlaylistItem.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router-dom'; import { ReactComponent as Play } from '../../../assets/play.svg'; -import { PlaylistType } from '../../../types/playlist.interface'; +import type { PlaylistType } from '../../../types/playlist.interface'; import styles from './PlaylistItem.module.scss'; const PlaylistItem = ({ playlist }: { playlist: PlaylistType }) => ( diff --git a/src/pages/Playlists/Playlists.tsx b/src/pages/Playlists/Playlists.tsx index 5603574..883fe24 100644 --- a/src/pages/Playlists/Playlists.tsx +++ b/src/pages/Playlists/Playlists.tsx @@ -1,5 +1,5 @@ -import { PlaylistType } from '../../types/playlist.interface'; -import { PlaylistsType } from '../../types/playlists.interface'; +import type { PlaylistType } from '../../types/playlist.interface'; +import type { PlaylistsType } from '../../types/playlists.interface'; import PlaylistItem from './PlaylistItem/PlaylistItem'; import styles from './Playlists.module.scss'; diff --git a/src/store/hooks.ts b/src/store/hooks.ts index 7c6fe6d..f76bd0c 100644 --- a/src/store/hooks.ts +++ b/src/store/hooks.ts @@ -1,4 +1,8 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; +import { + type TypedUseSelectorHook, + useDispatch, + useSelector, +} from 'react-redux'; import type { AppDispatch, RootState } from './store'; // Use throughout your app instead of plain `useDispatch` and `useSelector` diff --git a/src/store/reducers/currentSong.slice.ts b/src/store/reducers/currentSong.slice.ts index aeb46b9..23726e9 100644 --- a/src/store/reducers/currentSong.slice.ts +++ b/src/store/reducers/currentSong.slice.ts @@ -1,6 +1,6 @@ -import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { Track } from '../../types/track.interface'; -import { RootState } from '../store'; +import { type PayloadAction, createSlice } from '@reduxjs/toolkit'; +import type { Track } from '../../types/track.interface'; +import type { RootState } from '../store'; interface CurrentSongState { playing: boolean; diff --git a/src/store/reducers/featuredPlaylists.slice.ts b/src/store/reducers/featuredPlaylists.slice.ts index 9912c5e..2c75c04 100644 --- a/src/store/reducers/featuredPlaylists.slice.ts +++ b/src/store/reducers/featuredPlaylists.slice.ts @@ -1,7 +1,7 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { GetFeaturedPlaylists } from '../../API'; -import { PlaylistsType } from '../../types/playlists.interface'; -import { RootState } from '../store'; +import type { PlaylistsType } from '../../types/playlists.interface'; +import type { RootState } from '../store'; export const fetchFeaturedPlaylists = createAsyncThunk( 'playlists/fetchFeatured', diff --git a/src/store/reducers/playlistDetail.slice.ts b/src/store/reducers/playlistDetail.slice.ts index 3d95638..9e60e31 100644 --- a/src/store/reducers/playlistDetail.slice.ts +++ b/src/store/reducers/playlistDetail.slice.ts @@ -1,7 +1,7 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { GetPlaylistDetail } from '../../API'; -import { PlaylistType } from '../../types/playlist.interface'; -import { RootState } from '../store'; +import type { PlaylistType } from '../../types/playlist.interface'; +import type { RootState } from '../store'; export const fetchPlaylistById = createAsyncThunk( 'playlists/fetchById', diff --git a/src/types/playlist.interface.ts b/src/types/playlist.interface.ts index 86eaaa1..ae208a2 100644 --- a/src/types/playlist.interface.ts +++ b/src/types/playlist.interface.ts @@ -1,5 +1,5 @@ -import { Image } from './image.interface'; -import { Track } from './track.interface'; +import type { Image } from './image.interface'; +import type { Track } from './track.interface'; export interface PlaylistType { collaborative: boolean; diff --git a/src/types/playlists.interface.ts b/src/types/playlists.interface.ts index e335105..75e35cb 100644 --- a/src/types/playlists.interface.ts +++ b/src/types/playlists.interface.ts @@ -1,4 +1,4 @@ -import { PlaylistType } from './playlist.interface'; +import type { PlaylistType } from './playlist.interface'; export interface PlaylistsType { href: string; diff --git a/src/types/track.interface.ts b/src/types/track.interface.ts index 270dfbd..ae6136e 100644 --- a/src/types/track.interface.ts +++ b/src/types/track.interface.ts @@ -1,4 +1,4 @@ -import { Image } from './image.interface'; +import type { Image } from './image.interface'; interface Artist { external_urls: { spotify: string }; diff --git a/src/utils/useStopwatch.ts b/src/utils/useStopwatch.ts index 8bce93c..294bddb 100644 --- a/src/utils/useStopwatch.ts +++ b/src/utils/useStopwatch.ts @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useState } from 'react'; /** * Custom stopwatch diff --git a/yarn.lock b/yarn.lock index f5a05d7..a128f55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -275,47 +275,59 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@biomejs/biome@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.1.2.tgz#f747e0e5bf0a024e83fdd2ce9bab9c265e1634b6" - integrity sha512-JEVWchqo0Xhl86IJgOh0xESWnNRUXBUDByCBR8TA4lIPzm/6U6Tv77+MblNkZ8MvwCtP6PlBNGdQcGKKabtuHA== +"@biomejs/biome@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.6.0.tgz#b0f3bcc93451a7de74288af30f62f80aa74b1283" + integrity sha512-hvP8K1+CV8qc9eNdXtPwzScVxFSHB448CPKSqX6+8IW8G7bbhBVKGC80BowExJN5+vu+kzsj4xkWa780MAOlJw== optionalDependencies: - "@biomejs/cli-darwin-arm64" "1.1.2" - "@biomejs/cli-darwin-x64" "1.1.2" - "@biomejs/cli-linux-arm64" "1.1.2" - "@biomejs/cli-linux-x64" "1.1.2" - "@biomejs/cli-win32-arm64" "1.1.2" - "@biomejs/cli-win32-x64" "1.1.2" - -"@biomejs/cli-darwin-arm64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.1.2.tgz#33107bf22ba4428a19d33b100bf63d58fdb17627" - integrity sha512-YyqWeNZchPxlvxtdo2vMBkzrwllaNS3+DZ6j01mUCVIZE9kAzF/edMV2O38L2AEtnRLU1TI1f71Jai3ThILClg== - -"@biomejs/cli-darwin-x64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.1.2.tgz#a1556a2a8c7257afab15469036bfb77a64d2d8ac" - integrity sha512-Sofxcu50AHJyQS6Xx3OF2egQQ7Un5YFVF5/umNFa+kSNrrCu/ucmzrk8FcGS2dOSs4L2LqD6ZDWjvbcikjzLYQ== - -"@biomejs/cli-linux-arm64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.1.2.tgz#4daf1ca573ad8a9747ef50f72542de46bded0a0a" - integrity sha512-wtaQgpoVMZEKf1GlDlFGAJP1j6gnh4L4kJN8PQPOBAdKIUZ/YSjqVp0z28vli5xCQ57xCn1gH4Xoqw2gVYu1tQ== - -"@biomejs/cli-linux-x64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.1.2.tgz#25ed0d4720673848c829a50ad67538466364037c" - integrity sha512-TYIUjCXbY+kxnJgv8GESplMagB1GdOcMV21JGRATqnhUI4BvG6sjs3gfi+sdjLBQdbHhsISXW3yfUlv07HKqhg== - -"@biomejs/cli-win32-arm64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.1.2.tgz#4246b96c53c7de5997f1a19dd3e0717355978db9" - integrity sha512-yApn85KuJ+Ty5zxbqWnaifX4ONtZG+snu12RNKi8fxSVVCXzQ/k2PfsWQbsyvCG05qshSvNKtM54cuf+vhUIsw== - -"@biomejs/cli-win32-x64@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.1.2.tgz#2bd8d0c4ab6277baf3d4eedd5258caa972814152" - integrity sha512-qebNvIrFj2TJ+K0JVGo1HkgV2y5jis6aOZDC1SWuk53GnqjSLdR+p1v86ZByOjYr1v+tjc67EXmEepk06VVvpA== + "@biomejs/cli-darwin-arm64" "1.6.0" + "@biomejs/cli-darwin-x64" "1.6.0" + "@biomejs/cli-linux-arm64" "1.6.0" + "@biomejs/cli-linux-arm64-musl" "1.6.0" + "@biomejs/cli-linux-x64" "1.6.0" + "@biomejs/cli-linux-x64-musl" "1.6.0" + "@biomejs/cli-win32-arm64" "1.6.0" + "@biomejs/cli-win32-x64" "1.6.0" + +"@biomejs/cli-darwin-arm64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.6.0.tgz#60f3620ccb2aac618afd9cd119291afa69d4aabf" + integrity sha512-K1Fjqye5pt+Ua+seC7V/2bFjfnqOaEOcQbBQSiiefB/VPNOb6lA5NFIfJ1PskTA3JrMXE1k7iqKQn56qrKFS6A== + +"@biomejs/cli-darwin-x64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.6.0.tgz#dd3a0d66ff22f800bb13df33cb4dacf191eefe78" + integrity sha512-CjEALu6vN9RbcfhaBDoj481mesUIsUjxgQn+/kiMCea+Paypqslhez1I7OwRBJnkzz+Pa+PXdABd7S30eyy6+Q== + +"@biomejs/cli-linux-arm64-musl@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.0.tgz#cf2b171757f4f2062076ca323f2f23c1753208e9" + integrity sha512-prww6AUuJ+IO/GziN3WjtGM/DNOVuPFxqWrK97wKTZygEDdA+o1qHUN2HeCkSyk084xnzbMSbls5xscAKAn43A== + +"@biomejs/cli-linux-arm64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.6.0.tgz#a7f2761d8566b2fdc78e09fc0d85cf1fccce602e" + integrity sha512-32LVrC7dAgQT39YZ0ieO/VzzpAflozs9mW5K0oKNef7S4ocCdk89E98eXApxOdei0JTf3vfseDCl1AUIp6MwJw== + +"@biomejs/cli-linux-x64-musl@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.0.tgz#95e17927d498af868cca32e3be79132ddeb3036f" + integrity sha512-NwitWeUKCy8G/rr+rgHPYirnrsOjJEJBWODdaRzweeFNcJjvO6de6AmNdSJzsewzLEaxjOWyoXU03MdzbGz/6Q== + +"@biomejs/cli-linux-x64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.6.0.tgz#bcab0e71981a5601d85ea3ef09454b262764b776" + integrity sha512-b6mWu9Cu4w5B3K46wq9SlxKEZEEL6II/6HFNAuZ4YL8mOeQ0FTMU+wNMJFKkmkSE2zvim3xwW3PknmbLKbe3Mg== + +"@biomejs/cli-win32-arm64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.6.0.tgz#af94814be3c15ae4d201c01708a4ef79378cad36" + integrity sha512-DlNOL6mG+76iZS1gL/UiuMme7jnt+auzo2+u0aUq6UXYsb75juchwlnVLy2UV5CQjVBRB8+RM+KVoXRZ8NlBjQ== + +"@biomejs/cli-win32-x64@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.6.0.tgz#a90b549170864eff030a6b7e395f48ead5193170" + integrity sha512-sXBcXIOGuG8/XcHqmnkhLIs0oy6Dp+TkH4Alr4WH/P8mNsp5GcStI/ZwbEiEoxA0P3Fi+oUppQ6srxaY2rSCHg== "@esbuild/android-arm64@0.18.20": version "0.18.20" @@ -1073,10 +1085,10 @@ estree-walker@^2.0.2: resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -fast-average-color@^9.3.0: - version "9.3.0" - resolved "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.3.0.tgz" - integrity sha512-FlPROSqDMOnoBgkFhWMHJODPvpS0Od0WDpedcKq4U/t0JVapGAkblNwxOr75qT+ZNd0dQM4qlgqrtnXbCJ8cNg== +fast-average-color@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/fast-average-color/-/fast-average-color-9.4.0.tgz#eea0182fa8818ea0c70dcc7a85b945f3c716b11b" + integrity sha512-bvM8vV6YwK07dPbzFz77zJaBcfF6ABVfgNwaxVgXc2G+o0e/tzLCF9WU8Ryp1r0Nkk6JuJNsWCzbb4cLOMlB+Q== fast-glob@^3.2.7: version "3.3.1" @@ -1618,9 +1630,9 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== postcss@^8.4.27: - version "8.4.27" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz" - integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" picocolors "^1.0.0"