Skip to content

Commit

Permalink
feat: added box if band/artist stopped performing (#27)
Browse files Browse the repository at this point in the history
* refactor: used cached version of the search

* feat: added box if bands stopped playing

* refactor: improved ui tracks
  • Loading branch information
sirLisko authored Oct 6, 2024
1 parent 7421008 commit 7c9c591
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 53 deletions.
56 changes: 20 additions & 36 deletions components/Home/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,31 @@ import React, {
} from "react";
import { useRouter } from "next/router";
import { Search as SearchIcon, X as CloseIcon } from "lucide-react";

type Artist = {
id: string;
name: string;
disambiguation?: string;
};
import { useSearchArtistByName } from "services/searchArtist";
import { ArtistInfo } from "types";

const Search = () => {
const [searchTerm, setSearchTerm] = useState("");
const [suggestions, setSuggestions] = useState<Artist[]>([]);
const [suggestions, setSuggestions] = useState<ArtistInfo[]>([]);
const [selectedIndex, setSelectedIndex] = useState(-1);
const [isOpen, setIsOpen] = useState(false);
const search = useRef<HTMLInputElement>(null);
const wrapperRef = useRef<HTMLFormElement>(null);
const router = useRouter();

useEffect(() => {
const fetchSuggestions = async () => {
if (searchTerm.length > 1) {
try {
const response = await fetch(
`https://musicbrainz.org/ws/2/artist?query=${encodeURIComponent(searchTerm)}&fmt=json`,
);
const data = await response.json();
setSuggestions(data.artists.slice(0, 5));
setSelectedIndex(-1);
setIsOpen(true);
} catch (error) {
console.error("Error fetching suggestions:", error);
}
} else {
setSuggestions([]);
setSelectedIndex(-1);
setIsOpen(false);
}
};
const { data } = useSearchArtistByName(searchTerm);

const debounceTimer = setTimeout(fetchSuggestions, 300);
return () => clearTimeout(debounceTimer);
}, [searchTerm]);
useEffect(() => {
if (data && searchTerm.length > 1) {
const newSuggestions = data.artists.slice(0, 5);
setSuggestions(newSuggestions);
setSelectedIndex(-1);
setIsOpen(newSuggestions.length > 0);
} else if (searchTerm.length <= 1) {
setSuggestions([]);
setIsOpen(false);
}
}, [data, searchTerm]);

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
Expand All @@ -62,33 +47,32 @@ const Search = () => {
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [wrapperRef]);
}, []);

const onFormSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (selectedIndex >= 0 && selectedIndex < suggestions.length) {
handleSuggestionSelect(suggestions[selectedIndex]);
} else {
searchTerm && router.push("/[...artist]", `/${searchTerm}`);
} else if (searchTerm) {
router.push("/[...artist]", `/${searchTerm}`);
}
};

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
};

const handleSuggestionSelect = (suggestion: Artist) => {
const handleSuggestionSelect = (suggestion: ArtistInfo) => {
setSearchTerm(suggestion.name);
setSuggestions([]);
setIsOpen(false);
router.push("/[...artist]", `/${suggestion.name}/${suggestion.id}`);
};

const clearSearch = () => {
setSearchTerm("");
setSuggestions([]);
setSelectedIndex(-1);
setIsOpen(false);
setSuggestions([]);
if (search.current) {
search.current.focus();
}
Expand Down
47 changes: 34 additions & 13 deletions components/Result/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@ import SavePlaylist from "components/SavePlaylist/SavePlaylist";
import { useArtistData } from "services/artistData";
import { useTracks } from "services/tracks";
import { useEvents } from "services/events";
import { ArrowLeft, Frown } from "lucide-react";
import { ArrowLeft, Frown, TriangleAlert } from "lucide-react";
import Link from "next/link";
import { useGetArtist } from "services/searchArtist";

interface Props {
artist: string[];
artistQuery: string[];
}

const Result = ({ artist }: Props) => {
const Result = ({ artistQuery }: Props) => {
const [initialBaground] = useState<string>(document.body.style.background);
const { artistData, isLoading: isLoadingArtist } = useArtistData(artist[0]);
const { artistData, isLoading: isLoadingArtist } = useArtistData(
artistQuery[0],
);
const { tracks, isLoading: isLoadingTracks } = useTracks(
artist[0],
artist[1],
artistQuery[0],
artistQuery[1],
);
const { events } = useEvents(artist[0]);
const { artist } = useGetArtist(artistQuery?.[1]);
const { events } = useEvents(artistQuery[0]);

const from = `rgba(${artistData?.palette?.DarkVibrant.rgb.join(",")},100)`;

Expand Down Expand Up @@ -79,11 +83,28 @@ const Result = ({ artist }: Props) => {

{events && <Events events={events} />}

{/* <div className="bg-black bg-opacity-30 rounded-lg p-4 mb-6">
<h2 className="text-xl font-semibold mb-2">Playlist Info</h2>
<p>Based on last 20 concerts from 2022 to 2024</p>
<p>13 songs • Estimated playtime: 65 minutes</p>
</div> */}
{artist?.["life-span"].ended && (
<div className="bg-black bg-opacity-30 rounded-lg p-4 mb-6">
<h2 className="text-xl font-semibold mb-2 flex gap-1">
<TriangleAlert /> Important notice
</h2>
<p>
The data displayed may not be fully accurate as this artist or
band stopped performing on{" "}
<strong>
{new Date(artist["life-span"].end).toLocaleDateString(
undefined,
{
year: "numeric",
month: "long",
day: "numeric",
},
)}
</strong>
.
</p>
</div>
)}

<SavePlaylist artistData={artistData} tracks={tracks} />
<Tracks
Expand All @@ -98,7 +119,7 @@ const Result = ({ artist }: Props) => {
<Frown height={100} width={100} />
</div>
<div className="m-auto text-center text-2xl p-3">
No setlists found for <b>{artist[0]}</b>
No setlists found for <b>{artistQuery[0]}</b>
</div>
</div>
)}
Expand Down
5 changes: 2 additions & 3 deletions components/Tracks/Tracks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Track, Link, ArtistData } from "types";
import { isSameSong } from "utils/matchSongs";

import { Disc3 as Disc } from "lucide-react";
import classNames from "classnames";
import SpotifyLogo from "components/Icons/Spotify";
import PauseIcon from "components/Icons/Pause";
import PlayIcon from "components/Icons/Play";
Expand Down Expand Up @@ -102,7 +101,7 @@ const Tracks = ({ tracks, links, palette }: TracksProps) => {
{link?.previewUrl && (
<button
onClick={() => handlePreview(link.previewUrl, title)}
className={classNames("absolute left-0 top-0 h-full p-3")}
className="absolute left-0 top-0 h-full p-3 rounded-full bg-transparent hover:bg-black/50 transition-colors duration-300"
aria-pressed={isPlaying}
aria-label={isPlaying ? "Pause" : "Play"}
>
Expand All @@ -127,7 +126,7 @@ const Tracks = ({ tracks, links, palette }: TracksProps) => {
)}
<div className="text-sm opacity-75">
<span className="hidden md:inline">Played </span>
{count} times
<span className="whitespace-nowrap">{count} times</span>
</div>
</div>
</li>
Expand Down
2 changes: 1 addition & 1 deletion pages/[...artist].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ResultPage = () => {
<Loader height={80} width={80} ariaLabel="loading" color="white" />
</div>
) : (
<Result artist={artist} />
<Result artistQuery={artist} />
)}
{!isLoading && (
<Footer
Expand Down
44 changes: 44 additions & 0 deletions services/searchArtist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import useSWR from "swr";
import { ArtistInfo } from "types";
import { fetcher } from "utils/api";

export type SearchResults = {
artists: ArtistInfo[];
};

export const useSearchArtistByName = (searchTerm: string | undefined) => {
const { data, error, isLoading } = useSWR(
searchTerm && searchTerm.length > 1
? `https://musicbrainz.org/ws/2/artist?query=${encodeURIComponent(searchTerm)}&fmt=json`
: null,
fetcher<SearchResults>,
{
dedupingInterval: 300,
revalidateOnFocus: false,
revalidateOnReconnect: false,
},
);

return {
data,
isLoading,
isError: error,
};
};

export const useGetArtist = (mbid: string | undefined) => {
const { data, error, isLoading } = useSWR(
mbid ? `https://musicbrainz.org/ws/2/artist/${mbid}?fmt=json` : null,
fetcher<ArtistInfo>,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
},
);

return {
artist: data,
isLoading,
isError: error,
};
};
13 changes: 13 additions & 0 deletions types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface Palette {
rgb: [number, number, number];
}

// returned by Spotify
export interface ArtistData {
name: string;
image: string | undefined;
Expand All @@ -31,3 +32,15 @@ export interface ArtistData {
LightVibrant: Palette;
};
}

// returned by MusicBrainz
export interface ArtistInfo {
id: string;
name: string;
disambiguation: string;
"life-span": {
end: string;
begin: string;
ended: boolean;
};
}

0 comments on commit 7c9c591

Please sign in to comment.