A decentralized music tracking and discovery platform built on AT Protocol ๐ŸŽต
listenbrainz spotify atproto lastfm musicbrainz scrobbling

feat(web): add InteractionBar component and integrate with SongCover for 'like' functionality #11

merged opened by tsiry-sandratraina.com targeting main from feat/like-scrobble
Labels

None yet.

Participants 1
AT URI
at://did:plc:7vdlgi2bflelz7mmuxoqjfcr/sh.tangled.repo.pull/3lzw6bsiobj22
+77 -9
Interdiff #3 โ†’ #4
+22 -4
apps/web/src/components/SongCover/InteractionBar/InteractionBar.tsx
··· 1 1 import HeartOutline from "../../Icons/HeartOutline"; 2 2 import HeartFilled from "../../Icons/Heart"; 3 3 4 - function InteractionBar() { 4 + export interface InteractionBarProps { 5 + likesCount: number; 6 + liked: boolean; 7 + onLike: () => void; 8 + } 9 + 10 + function InteractionBar({ likesCount, liked, onLike }: InteractionBarProps) { 5 11 return ( 6 12 <div className="absolute bottom-[-1px] left-0 h-[100px] w-full bg-[linear-gradient(rgba(22,24,35,0)_2.92%,rgba(22,24,35,0.5)_98.99%)] flex justify-start items-end p-[10px] rounded-b-[8px]"> 7 13 <div className="h-[40px] w-full flex items-center"> 8 - <span className="cursor-pointer" onClick={(e) => e.preventDefault()}> 9 - {true && <HeartOutline color="#fff" />} 10 - {false && <HeartFilled color="#fff" />} 14 + <span 15 + className="cursor-pointer" 16 + onClick={(e) => { 17 + e.stopPropagation(); 18 + e.preventDefault(); 19 + onLike(); 20 + }} 21 + > 22 + {!liked && <HeartOutline color="#fff" />} 23 + {liked && <HeartFilled color="#fff" />} 11 24 </span> 25 + {likesCount > 0 && ( 26 + <span className="ml-[5px] mt-[-4px] text-sm text-white"> 27 + {likesCount} 28 + </span> 29 + )} 12 30 </div> 13 31 </div> 14 32 );
apps/web/src/components/SongCover/InteractionBar/index.tsx

This file has not been changed.

+44 -3
apps/web/src/components/SongCover/SongCover.tsx
··· 1 1 import { css } from "@emotion/react"; 2 2 import styled from "@emotion/styled"; 3 3 import InteractionBar from "./InteractionBar"; 4 + import useLike from "../../hooks/useLike"; 5 + import SignInModal from "../SignInModal"; 6 + import { useState } from "react"; 4 7 5 8 const Cover = styled.img<{ size?: number }>` 6 9 border-radius: 8px; ··· 51 54 52 55 export type SongCoverProps = { 53 56 cover: string; 57 + uri?: string; 54 58 title?: string; 55 59 artist?: string; 56 60 size?: number; 61 + liked?: boolean; 62 + likesCount?: number; 57 63 withLikeButton?: boolean; 58 64 }; 59 65 60 66 function SongCover(props: SongCoverProps) { 61 - const { title, artist, cover, size, withLikeButton } = props; 67 + const [isSignInOpen, setIsSignInOpen] = useState(false); 68 + const [liked, setLiked] = useState(props.liked); 69 + const { like, unlike } = useLike(); 70 + const [likesCount, setLikesCount] = useState(props.likesCount); 71 + const { title, artist, cover, size, uri, withLikeButton } = props; 72 + const handleLike = async () => { 73 + if (!uri) return; 74 + if (!localStorage.getItem("token")) { 75 + setIsSignInOpen(true); 76 + return; 77 + } 78 + if (liked) { 79 + setLiked(false); 80 + if (likesCount !== undefined && likesCount > 0) { 81 + setLikesCount(likesCount - 1); 82 + } 83 + await unlike(uri); 84 + } else { 85 + setLiked(true); 86 + if (likesCount !== undefined) { 87 + setLikesCount(likesCount + 1); 88 + } 89 + await like(uri); 90 + } 91 + }; 62 92 return ( 63 - <CoverWrapper> 93 + <CoverWrapper onClick={(e) => e.stopPropagation()}> 64 94 <div className={`relative h-[100%] w-[92%]`}> 65 - {withLikeButton && <InteractionBar />} 95 + {withLikeButton && ( 96 + <InteractionBar 97 + liked={!!liked} 98 + likesCount={likesCount || 0} 99 + onLike={handleLike} 100 + /> 101 + )} 66 102 <Cover src={cover} size={size} /> 67 103 </div> 68 104 <div className="mb-[13px] mt-[10px]"> ··· 71 107 </SongTitle> 72 108 <Artist>{artist}</Artist> 73 109 </div> 110 + <SignInModal 111 + isOpen={isSignInOpen} 112 + onClose={() => setIsSignInOpen(false)} 113 + like 114 + /> 74 115 </CoverWrapper> 75 116 ); 76 117 }
+3
apps/web/src/pages/home/feed/Feed.tsx
··· 126 126 className="no-underline text-[var(--color-text-primary)]" 127 127 > 128 128 <SongCover 129 + uri={song.trackUri} 129 130 cover={song.cover} 130 131 artist={song.artist} 131 132 title={song.title} 133 + liked={song.liked} 134 + likesCount={song.likesCount} 132 135 withLikeButton 133 136 /> 134 137 </Link>
+3
apps/web/src/api/feed.ts
··· 79 79 uri: string; 80 80 albumUri: string; 81 81 artistUri: string; 82 + trackUri: string; 82 83 xataVersion: number; 83 84 cover: string; 84 85 date: string; ··· 86 87 userDisplayName: string; 87 88 userAvatar: string; 88 89 tags: string[]; 90 + likesCount: number; 91 + liked: boolean; 89 92 id: string; 90 93 }; 91 94 }[];
+5 -2
apps/web/src/components/SignInModal/SignInModal.tsx
··· 7 7 interface SignInModalProps { 8 8 isOpen: boolean; 9 9 onClose: () => void; 10 + like?: boolean; 10 11 } 11 12 12 13 function SignInModal(props: SignInModalProps) { 13 - const { isOpen, onClose } = props; 14 + const { isOpen, onClose, like } = props; 14 15 const [handle, setHandle] = useState(""); 15 16 16 17 const onLogin = async () => { ··· 55 56 <ModalBody style={{ padding: 10 }}> 56 57 <h1 style={{ color: "#ff2876", textAlign: "center" }}>Rocksky</h1> 57 58 <p className="text-[var(--color-text)] text-[18px] mt-[40px] mb-[20px]"> 58 - Sign in or create your account to join the conversation! 59 + {!like 60 + ? "Sign in or create your account to join the conversation!" 61 + : "Sign in or create your account to like songs!"} 59 62 </p> 60 63 <div style={{ marginBottom: 20 }}> 61 64 <div style={{ marginBottom: 15 }}>

Submissions

sign up or login to add to the discussion
tsiry-sandratraina.com submitted #4
4 commits
expand
feat: add InteractionBar component and integrate with SongCover for like functionality
work in progress
Add likes support to feed and SongCover
Prompt sign-in for likes and stop click propagation
pull request successfully merged
tsiry-sandratraina.com submitted #3
2 commits
expand
feat: add InteractionBar component and integrate with SongCover for like functionality
work in progress
tsiry-sandratraina.com submitted #2
1 commit
expand
feat: add InteractionBar component and integrate with SongCover for like functionality
tsiry-sandratraina.com submitted #1
1 commit
expand
feat: add InteractionBar component and integrate with SongCover for like functionality
tsiry-sandratraina.com submitted #0
1 commit
expand
feat: add InteractionBar component and integrate with SongCover for like functionality