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
+102 -8
Diff #4
+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 }}>
+35
apps/web/src/components/SongCover/InteractionBar/InteractionBar.tsx
··· 1 + import HeartOutline from "../../Icons/HeartOutline"; 2 + import HeartFilled from "../../Icons/Heart"; 3 + 4 + export interface InteractionBarProps { 5 + likesCount: number; 6 + liked: boolean; 7 + onLike: () => void; 8 + } 9 + 10 + function InteractionBar({ likesCount, liked, onLike }: InteractionBarProps) { 11 + return ( 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]"> 13 + <div className="h-[40px] w-full flex items-center"> 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" />} 24 + </span> 25 + {likesCount > 0 && ( 26 + <span className="ml-[5px] mt-[-4px] text-sm text-white"> 27 + {likesCount} 28 + </span> 29 + )} 30 + </div> 31 + </div> 32 + ); 33 + } 34 + 35 + export default InteractionBar;
+3
apps/web/src/components/SongCover/InteractionBar/index.tsx
··· 1 + import InteractionBar from "./InteractionBar"; 2 + 3 + export default InteractionBar;
+51 -5
apps/web/src/components/SongCover/SongCover.tsx
··· 1 1 import { css } from "@emotion/react"; 2 2 import styled from "@emotion/styled"; 3 + import InteractionBar from "./InteractionBar"; 4 + import useLike from "../../hooks/useLike"; 5 + import SignInModal from "../SignInModal"; 6 + import { useState } from "react"; 3 7 4 8 const Cover = styled.img<{ size?: number }>` 5 9 border-radius: 8px; 6 10 height: 240px; 7 11 width: 240px; 8 - margin-bottom: 10px; 12 + margin-bottom: -5px; 9 13 ${(props) => 10 14 props.size && 11 15 css` ··· 50 54 51 55 export type SongCoverProps = { 52 56 cover: string; 57 + uri?: string; 53 58 title?: string; 54 59 artist?: string; 55 60 size?: number; 61 + liked?: boolean; 62 + likesCount?: number; 63 + withLikeButton?: boolean; 56 64 }; 57 65 58 66 function SongCover(props: SongCoverProps) { 59 - const { title, artist, cover, size } = 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 + }; 60 92 return ( 61 - <CoverWrapper> 62 - <Cover src={cover} size={size} /> 63 - <div className="mb-[13px]"> 93 + <CoverWrapper onClick={(e) => e.stopPropagation()}> 94 + <div className={`relative h-[100%] w-[92%]`}> 95 + {withLikeButton && ( 96 + <InteractionBar 97 + liked={!!liked} 98 + likesCount={likesCount || 0} 99 + onLike={handleLike} 100 + /> 101 + )} 102 + <Cover src={cover} size={size} /> 103 + </div> 104 + <div className="mb-[13px] mt-[10px]"> 64 105 <SongTitle className="!text-[var(--color-text-primary)]"> 65 106 {title} 66 107 </SongTitle> 67 108 <Artist>{artist}</Artist> 68 109 </div> 110 + <SignInModal 111 + isOpen={isSignInOpen} 112 + onClose={() => setIsSignInOpen(false)} 113 + like 114 + /> 69 115 </CoverWrapper> 70 116 ); 71 117 }
+5 -1
apps/web/src/pages/home/feed/Feed.tsx
··· 76 76 } 77 77 console.log(">> WebSocket connection closed"); 78 78 }; 79 - }, [queryClient]); 79 + }, [queryClient, feedUri]); 80 80 81 81 return ( 82 82 <Container> ··· 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} 135 + withLikeButton 132 136 /> 133 137 </Link> 134 138 <div className="flex">

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