├── .gitignore
├── assets
├── add.svg
├── album.jpg
├── avatar.jpg
├── chevronLeft.svg
├── chevronRight.svg
├── disc.svg
├── download.svg
├── friend.svg
├── heart.svg
├── home.svg
├── more.svg
├── next.svg
├── pause.svg
├── play.svg
├── playRounded.svg
├── playlist.svg
├── previous.svg
├── repeat.svg
├── search.svg
├── shuffle.svg
├── speaker.svg
└── time.svg
├── components
├── UploadModal.js
├── activity.js
├── activityCard.js
├── header.js
├── login.js
├── nav.js
├── navLink.js
├── payment.js
├── playerControls.js
├── playlist.js
├── table
│ └── tableRow.js
└── uploadButton.js
├── context
├── WalletConnectionProvider.js
└── context.js
├── data
├── activities.js
└── songs.js
├── hooks
└── useSpotify.js
├── next.config.js
├── package.json
├── pages
├── _app.js
├── api
│ └── upload_music.js
├── homepage.js
└── index.js
├── postcss.config.js
├── public
├── assets
│ ├── add.svg
│ ├── album.jpg
│ ├── avatar.jpg
│ ├── chevronLeft.svg
│ ├── chevronRight.svg
│ ├── disc.svg
│ ├── download.svg
│ ├── friend.svg
│ ├── heart.svg
│ ├── home.svg
│ ├── more.svg
│ ├── next.svg
│ ├── play.svg
│ ├── playRounded.svg
│ ├── playlist.svg
│ ├── previous.svg
│ ├── repeat.svg
│ ├── search.svg
│ ├── shuffle.svg
│ ├── speaker.svg
│ └── time.svg
├── favicon.ico
└── vercel.svg
├── readme.md
├── styles
├── Home.module.css
├── UploadModal.module.css
└── globals.css
├── tailwind.config.js
├── utils
├── const.js
├── spotify.json
└── utils.js
└── wallet.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | # next.js
12 | out/
13 | ./next
14 | .next/
15 | .next
16 |
17 | # production
18 | build
19 |
20 | # misc
21 | .DS_Store
22 | *.pem
23 |
24 | # debug
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 | .pnpm-debug.log*
29 |
30 | # local env files
31 | .env.local
32 | .env.development.local
33 | .env.test.local
34 | .env.production.local
35 |
36 | # vercel
37 | .vercel
38 |
39 | # snyk code quality
40 | .dccache
41 |
42 | # lock files
43 | yarn.lock
44 | package-lock.json
45 |
46 | target
47 |
48 | *.wav
--------------------------------------------------------------------------------
/assets/add.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/album.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/spotify-clone-blockchain/afe6e4409227b097d10c65323b8e87b2b7eece4b/assets/album.jpg
--------------------------------------------------------------------------------
/assets/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/spotify-clone-blockchain/afe6e4409227b097d10c65323b8e87b2b7eece4b/assets/avatar.jpg
--------------------------------------------------------------------------------
/assets/chevronLeft.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/chevronRight.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/disc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/friend.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/more.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/pause.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/playRounded.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/playlist.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/previous.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/repeat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/shuffle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/speaker.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/UploadModal.js:
--------------------------------------------------------------------------------
1 | import style from '../styles/UploadModal.module.css'
2 | import axios from 'axios'
3 |
4 | const UploadModal = ({
5 | description: title,
6 | musicUrl,
7 | newMusic,
8 | setTitle,
9 | setMusicUrl,
10 | setShowUploadMusic,
11 | }) => {
12 | const toBase64 = file =>
13 | new Promise((resolve, reject) => {
14 | const reader = new FileReader()
15 | reader.readAsDataURL(file)
16 | reader.onload = () => resolve(reader.result)
17 | reader.onerror = error => reject(error)
18 | })
19 |
20 | const uploadClicked = async () => {
21 | var files = document.querySelector('#music-file')
22 |
23 | if (files.files.length == 0) return
24 |
25 | const base64_file = await toBase64(files.files[0])
26 |
27 | axios
28 | .post(
29 | '/api/upload_music',
30 | { file: base64_file, filename: files.files[0].name },
31 | {},
32 | )
33 | .then(res => {
34 | console.log(res.data)
35 | if (
36 | res.data.result &&
37 | res.data.result.created &&
38 | res.data.result.created[0].dataTxId
39 | )
40 | setMusicUrl(
41 | 'https://arweave.net/' + res.data.result.created[0].dataTxId,
42 | )
43 | })
44 | .catch(err => {
45 | console.log(err)
46 | })
47 | }
48 |
49 | const createNewClicked = () => {
50 | newMusic()
51 | }
52 |
53 | return (
54 |
55 |
Upload New Music
56 |
57 |
58 |
64 |
65 |
66 |
67 |
Title
68 |
69 | setTitle(e.target.value)}
74 | />
75 |
76 |
77 |
78 |
Music Url
79 |
80 | setMusicUrl(e.target.value)}
85 | />
86 |
87 |
88 |
89 |
95 |
101 |
102 |
103 | )
104 | }
105 |
106 | export default UploadModal
107 |
--------------------------------------------------------------------------------
/components/activity.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import friend from '../assets/friend.svg'
3 | import { activities } from '../data/activities'
4 | import ActivityCard from './activityCard'
5 |
6 | const styles = {
7 | activity: `bg-black w-96 h-screen p-5 py-10 text-white`,
8 | title: `flex items-center justify-between mb-10`,
9 | profileAvatarContainer: `w-14 h-14 rounded-full -ml-2 mr-3`,
10 | activityCard: `flex mb-6`,
11 | }
12 |
13 | const Activity = () => {
14 | return (
15 |
16 |
17 | Friend Activity
18 |

19 |
20 |
21 |
22 | {activities.map((activity, index) => {
23 | return (
24 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
37 | export default Activity
38 |
--------------------------------------------------------------------------------
/components/activityCard.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 |
3 | const ActivityCard = ({ title, subTitle, avatar }) => {
4 | return (
5 |
6 |
7 |
14 |
15 |
16 |
{title}
17 |
{subTitle}
18 |
19 |
20 | )
21 | }
22 |
23 | export default ActivityCard
24 |
25 | const styles = {
26 | profileAvatarContainer: `w-14 h-14 rounded-full -ml-2 mr-3`,
27 | activityCard: `flex mb-6 cursor-pointer hover:opacity-50`,
28 | avatar: `rounded-full object-cover`,
29 | }
30 |
--------------------------------------------------------------------------------
/components/header.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { SpotifyContext } from '../context/context'
3 | import Image from 'next/image'
4 | import UploadButton from './uploadButton'
5 |
6 | const style = {
7 | arrowButton: `bg-black mr-2 w-10 h-10 flex items-center justify-center rounded-full bg-opacity-50 cursor-pointer hover:bg-opacity-75`,
8 | headerRight: `flex`,
9 | profile: `flex items-center bg-black rounded-full p-1 px-3 bg-opacity-50 cursor-pointer hover:bg-opacity-75`,
10 | iconContainer: `ml-10`,
11 | title: `text-6xl font-extrabold`,
12 | header: `max-w-7xl m-auto p-3`,
13 | headerWrapper: `flex items-center justify-between`,
14 | playlistTextContent: `flex items-end mt-10`,
15 | profileAvatarContainer: `w-7 h-7 rounded-full -ml-2 mr-3`,
16 | controlsContainer: `flex items-center mt-10`,
17 | playButton: `bg-green-500 w-16 h-16 flex pl-2 items-center justify-center rounded-full cursor-pointer`,
18 | }
19 |
20 | const Header = ({ setShowUploadMusic }) => {
21 | const { currentSong } = useContext(SpotifyContext)
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |

29 |
30 |
31 |

32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |

41 |
42 |
Your Name
43 |
44 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
ALBUM
57 |
Your Album
58 |
59 |
60 |

61 |
62 |
63 | SteamBeats • 2020 • 46 songs, 3
64 | hr 20 min
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

73 |
74 |
75 |

76 |
77 |
78 |

79 |
80 |
81 |

82 |
83 |
84 |
85 | )
86 | }
87 |
88 | export default Header
89 |
--------------------------------------------------------------------------------
/components/login.js:
--------------------------------------------------------------------------------
1 | import { useWallet } from '@solana/wallet-adapter-react'
2 | import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'
3 | import { Payment } from './payment'
4 |
5 | const Login = () => {
6 | const wallet = useWallet()
7 |
8 | /** show payment UI if wallet is connected */
9 | if (wallet.connected) return
10 |
11 | return (
12 |
13 |
Login to access this app
14 |
15 |
16 | )
17 | }
18 |
19 | export default Login
20 |
21 | const styles = {
22 | loginPage: `w-screen h-screen bg-white flex justify-center flex-col items-center`,
23 | text: `text-4xl text-black mb-10`,
24 | }
25 |
--------------------------------------------------------------------------------
/components/nav.js:
--------------------------------------------------------------------------------
1 | import NavLink from './navLink'
2 |
3 | const styles = {
4 | nav: `bg-black h-screen w-96 p-5 py-10 `,
5 | link: `hover:text-[#fff]`,
6 | playlistName: `text-[#b3b3b3] cursor-default text-sm hover:text-[#fff]`
7 | }
8 |
9 | const Nav = () => {
10 | return (
11 |
12 |
13 |
14 |
19 |
24 |
25 |
26 |
27 |
32 |
37 |
38 |
39 |
40 |
GHOST SONGS
41 |
42 | CarPlay Vol.2
43 |
44 |
45 | Country Dump
46 |
47 |
48 | Energy Booster: Country
49 |
50 |
51 | Funky
52 |
53 |
54 | Coaching 🔥
55 |
56 |
57 | Country
58 |
59 |
60 | Your Top Songs 2019
61 |
62 |
63 |
64 | )
65 | }
66 |
67 | export default Nav
68 |
--------------------------------------------------------------------------------
/components/navLink.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 |
3 | const styles = {
4 | navLink: `flex item-center mb-5 cursor-pointer hover:text-[#fff] text-[#b3b3b3]`,
5 | navLinkText: `ml-5`,
6 | }
7 |
8 | const NavLink = ({ title, icon }) => {
9 | return (
10 |
11 |

12 |
{title}
13 |
14 | )
15 | }
16 |
17 | export default NavLink
18 |
--------------------------------------------------------------------------------
/components/payment.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { getProgramInstance } from '../utils/utils'
3 | import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
4 | import { SOLANA_HOST } from '../utils/const'
5 | import { PublicKey } from '@solana/web3.js'
6 | import { useWallet } from '@solana/wallet-adapter-react'
7 | import HomePage from '../pages/homepage'
8 |
9 | const anchor = require('@project-serum/anchor')
10 |
11 | const { web3 } = anchor
12 | const { SystemProgram } = web3
13 | const utf8 = anchor.utils.bytes.utf8
14 |
15 | const defaultAccounts = {
16 | tokenProgram: TOKEN_PROGRAM_ID,
17 | clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
18 | systemProgram: SystemProgram.programId,
19 | }
20 |
21 | export const Payment = () => {
22 | const wallet = useWallet()
23 | const connection = new anchor.web3.Connection(SOLANA_HOST)
24 | const program = getProgramInstance(connection, wallet)
25 | const [payers, setPayers] = useState([])
26 | const [isPaid, setPaid] = useState(false)
27 |
28 | useEffect(() => {
29 | if (wallet.connected) getAllWallets()
30 | }, [wallet.connected, isPaid])
31 |
32 | const getAllWallets = async () => {
33 | const payerList = await program.account.payerAccount.all()
34 | setPayers(payerList)
35 |
36 | payerList.forEach(payer => {
37 | if (payer.account.wallet.toBase58() == wallet.publicKey.toBase58())
38 | setPaid(true)
39 | })
40 | }
41 |
42 | const payClicked = async () => {
43 | let [payerSigner] = await anchor.web3.PublicKey.findProgramAddress(
44 | [utf8.encode('payer'), wallet.publicKey.toBuffer()],
45 | program.programId,
46 | )
47 |
48 | let payerInfo
49 |
50 | try {
51 | payerInfo = await program.account.payerAccount.fetch(payerSigner)
52 | } catch (e) {
53 | try {
54 | await program.rpc.acceptPayment({
55 | accounts: {
56 | payerWallet: payerSigner,
57 | receiver: new PublicKey(
58 | 'FdGbqLGZQgTpqXc1bKa41YsJTWHPxfwZKmTykjG6Jj1V',
59 | ),
60 | authority: wallet.publicKey,
61 | ...defaultAccounts,
62 | },
63 | })
64 | alert('Transaction proceed')
65 | } catch (e) {
66 | alert(e.message)
67 | }
68 | }
69 | }
70 |
71 | /** show homepage if user makes payment */
72 | if (isPaid) return
73 |
74 | /** Payment Component */
75 | return (
76 |
77 |
Make payment
78 |
79 |
86 |
89 |
90 |
91 | )
92 | }
93 |
94 | const styles = {
95 | main: `w-screen h-screen bg-white text-black flex flex-col justify-center items-center`,
96 | button: `bg-[#22C55E] m-3 text-white font-bold py-4 px-7 rounded-full hover:opacity-70 transition`,
97 | text: `text-4xl text-black mb-10`,
98 | buttons: `flex items-center`,
99 | }
100 |
--------------------------------------------------------------------------------
/components/playerControls.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react"
2 | import { SpotifyContext } from "../context/context"
3 | import Image from "next/image"
4 | import next from "../assets/next.svg"
5 | import previous from "../assets/previous.svg"
6 | import speaker from "../assets/speaker.svg"
7 | import repeat from "../assets/repeat.svg"
8 | import shuffle from "../assets/shuffle.svg"
9 | import playRounded from "../assets/playRounded.svg"
10 | import pauseIcon from "../assets/pause.svg"
11 |
12 | const PlayerControls = ({songs}) => {
13 | const { currentSong, isPlaying, volume, onVolumeChange, timestamp, progress, playNext, playPrevious, isPaused, play, pause, onProgressChange } = useContext(SpotifyContext)
14 |
15 | if (!isPlaying) return null
16 |
17 | /** Show component if song is playing */
18 | return (
19 |
20 |
21 |
22 |
30 |
31 |
32 |
{currentSong.title}
33 |
{'artist'}
34 | {/*
{currentSong.artiste}
*/}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
playPrevious(songs)} className={styles.controlIcon}>
43 |
44 |
45 |
46 | {isPaused ?
47 | :
}
48 |
49 |
playNext(songs)} className={styles.controlIcon}>
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {timestamp}
58 | onProgressChange(e)}
61 | type='range'
62 | className={styles.range}
63 | />
64 | {'2:43'}
65 | {/* {currentSong.songLength} */}
66 |
67 |
68 |
69 |
70 |
71 |
72 | onVolumeChange(e)} type='range' id='volume-range' />
73 |
74 |
75 |
76 | )
77 | }
78 |
79 | export default PlayerControls
80 |
81 | const styles = {
82 | albumCoverContainer: `w-20 h-20 mr-3`,
83 | coverPhoto: `object-cover`,
84 | mainControl: `fixed bottom-0 left-0 p-5 py-3 pr-10 w-screen bg-[#242424] z-40 flex items-center justify-between`,
85 | // range: `appearance-none mx-3 hover:bg-[#000] h-1 hover:bg-[#22C55E] bg-[#fff] w-[500px]`,
86 | // volumeRange: `mx-3 -hue-rotate-90 h-1`,
87 | flexCenter: `flex items-center`,
88 | controlIcon: `mr-5 cursor-pointer hover:opacity-100 opacity-50`,
89 | playIcon: `mr-5 w-10 h-10 cursor-pointer hover:opacity-50`,
90 | pauseIconStyle: `mt-3 w-10 h-10 cursor-pointer hover:opacity-50`,
91 | controlIconsContainer: `flex items-center justify-center mb-2`,
92 | }
--------------------------------------------------------------------------------
/components/playlist.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import TableRow from './table/tableRow'
3 |
4 | const Playlist = ({songs}) => {
5 | return (
6 |
7 |
8 |
9 |
10 | # |
11 | TITLE |
12 | PLAYS |
13 |
14 |
15 | |
16 |
17 |
18 |
19 |
20 | {songs.map(song => {
21 | return
22 | })}
23 |
24 |
25 | )
26 | }
27 |
28 | export default Playlist
29 |
30 | const styles = {
31 | table: `w-full text-left`,
32 | tableWrapper: `max-w-7xl m-auto p-3 mt-5 mb-40`,
33 | tableHeader: `border-b border-gray-100/20 pb-5 opacity-50`,
34 | }
35 |
--------------------------------------------------------------------------------
/components/table/tableRow.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { SpotifyContext } from '../../context/context'
3 |
4 | const TableRow = ({ props }) => {
5 | const { playOnSelect } = useContext(SpotifyContext)
6 |
7 | return (
8 |
9 | playOnSelect(props)}>
10 | {props.index} |
11 |
12 |
13 | {props.title}
14 | {'artist'}
15 | {/* {props.artiste} */}
16 |
17 | |
18 | {'10,000'} |
19 | {/* {props.plays} | */}
20 | {'2:43'} |
21 | {/*{props.songLength} | */}
22 |
23 |
24 | )
25 | }
26 |
27 | export default TableRow
28 |
29 | const styles = {
30 | th: `pb-5 hover:opacity-50 cursor-pointer`,
31 | }
32 |
--------------------------------------------------------------------------------
/components/uploadButton.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { SpotifyContext } from '../context/context'
3 |
4 | const styles = {
5 | uploadButton: `bg-green-500 mr-10 px-3 py-1.5 cursor-pointer hover:scale-95 transition rounded-full`,
6 | }
7 |
8 | const UploadButton = ({ setShowUploadMusic }) => {
9 | // const { uploadMusic } = useContext(SpotifyContext)
10 |
11 | const uploadClicked = () => {
12 | console.log(setShowUploadMusic)
13 | setShowUploadMusic(true)
14 | }
15 |
16 | return (
17 |
18 |
19 | Upload music
20 |
21 |
22 | )
23 | }
24 |
25 | export default UploadButton
26 |
--------------------------------------------------------------------------------
/context/WalletConnectionProvider.js:
--------------------------------------------------------------------------------
1 | import { WalletAdapterNetwork } from '@solana/wallet-adapter-base'
2 | import {
3 | ConnectionProvider,
4 | WalletProvider,
5 | } from '@solana/wallet-adapter-react'
6 | import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'
7 | import { PhantomWalletAdapter } from '@solana/wallet-adapter-wallets'
8 | import { clusterApiUrl } from '@solana/web3.js'
9 | import { FC, useMemo } from 'react'
10 | import { CLUSTER, SOLANA_HOST } from '../utils/const'
11 |
12 | const WalletConnectionProvider = ({ children }) => {
13 | // const network = process.env.NEXT_PUBLIC_SOLANA_NETWORK as WalletAdapterNetwork;
14 | const endpoint = useMemo(() => SOLANA_HOST, [])
15 |
16 | const wallets = useMemo(() => [new PhantomWalletAdapter()], [])
17 |
18 | return (
19 |
20 |
21 | {children}
22 |
23 |
24 | )
25 | }
26 |
27 | export default WalletConnectionProvider
28 |
--------------------------------------------------------------------------------
/context/context.js:
--------------------------------------------------------------------------------
1 | import { createContext, useState, useEffect } from "react"
2 |
3 | export const SpotifyContext = createContext()
4 |
5 | export const SpotifyProvider = ({ children }) => {
6 | const [currentSong, setCurrentSong] = useState({})
7 | const [isPlaying, setIsPlaying] = useState(false)
8 | const [isPaused, setIsPaused] = useState(false)
9 | const [progress, setProgress] = useState(false)
10 | const [volume, setVolume] = useState(false)
11 | const [timestamp, setTimestamp] = useState()
12 |
13 | useEffect(() => {
14 | if (isPlaying) {
15 | let audio = document.querySelector('#audio-element')
16 |
17 | audio.addEventListener("timeupdate", function () {
18 | setTimestamp(secondsToMinSec(audio.currentTime))
19 | }, false)
20 | }
21 | }, [isPlaying])
22 |
23 | const pause = () => {
24 | setIsPaused(true)
25 | document.querySelector('#audio-element').pause()
26 | }
27 |
28 | const play = () => {
29 | document.querySelector('#audio-element').play()
30 | setIsPaused(false)
31 | }
32 |
33 | const playOnSelect = (song) => {
34 | try {
35 | document.querySelector('#audio-element').src = song.musicUrl
36 | document.querySelector('#audio-element').play()
37 | setCurrentSong(song)
38 | setIsPlaying(true)
39 | setIsPaused(false)
40 | } catch (e) { }
41 | }
42 |
43 | const secondsToMinSec = (value) => {
44 | const minute = Math.round(value / 60);
45 | let second = Math.round(value % 60);
46 | second = second >= 10 ? second : '0' + second;
47 | return minute + ':' + second;
48 | }
49 |
50 | const updateProgress = e => {
51 | const _progress = e.target.currentTime / e.target.duration
52 | setProgress(_progress.toFixed(2) * 100)
53 | }
54 |
55 | const updateVolume = (e) => {
56 | try {
57 | setVolume(e.target.value)
58 | document.querySelector('#audio-element').volume = e.target.value
59 | } catch (e) { }
60 | }
61 |
62 | const onProgressChange = (e) => {
63 | const _progress = e.target.value / 100
64 | document.querySelector('#audio-element').currentTime = _progress * document.querySelector('#audio-element').duration
65 | }
66 |
67 | const onVolumeChange = (e) => {
68 | const _volume = e.target.value / 100
69 | document.querySelector('#audio-element').volume = _volume
70 |
71 | }
72 |
73 | const playNext = (songs) => {
74 | const id = songs.findIndex(value => value.account === currentSong);
75 | if (songs.length === id + 1) {
76 | playOnSelect(songs[0].account)
77 | setCurrentSong(songs[0].account)
78 | return
79 | }
80 | const nextSong = songs[id + 1];
81 | playOnSelect(nextSong.account);
82 | }
83 |
84 | const playPrevious = (songs) => {
85 | const id = songs.findIndex(value => value.account === currentSong);
86 | if (id === 0) {
87 | playOnSelect(songs[songs.length - 1].account)
88 | setCurrentSong(songs[songs.length - 1].account)
89 | return
90 | }
91 | const previousSong = songs[id - 1];
92 | playOnSelect(previousSong.account);
93 | }
94 | return
109 | {children}
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/data/activities.js:
--------------------------------------------------------------------------------
1 | export const activities = [
2 | {
3 | index: 1,
4 | title: 'Ambient Gold',
5 | subTitle: 'Lo-Fi',
6 | avatar:
7 | 'https://kajabi-storefronts-production.kajabi-cdn.com/kajabi-storefronts-production/themes/284832/settings_images/rLlCifhXRJiT0RoN2FjK_Logo_roundbackground_black.png',
8 | },
9 | {
10 | index: 2,
11 | title: 'Kill it anyway',
12 | subTitle: 'SteamBeats originals',
13 | avatar:
14 | 'https://upload.wikimedia.org/wikipedia/commons/3/3b/Javascript_Logo.png',
15 | },
16 | {
17 | index: 3,
18 | title: 'Gift of a Guest',
19 | subTitle: 'SteamBeats originals',
20 | avatar: 'https://i.ytimg.com/vi/Yaeu73hr_eM/maxresdefault.jpg',
21 | },
22 | ]
23 |
--------------------------------------------------------------------------------
/data/songs.js:
--------------------------------------------------------------------------------
1 | export const songs = [
2 | {
3 | index: 0,
4 | title: 'Ambient Gold',
5 | artiste: 'Lo-Fi',
6 | plays: '20,382',
7 | songLength: '2:43',
8 | album: 'Songs & Beats',
9 | cover:
10 | 'https://kajabi-storefronts-production.kajabi-cdn.com/kajabi-storefronts-production/themes/284832/settings_images/rLlCifhXRJiT0RoN2FjK_Logo_roundbackground_black.png',
11 | arweaveLink:
12 | 'https://tnrapvewequ7ofz7idqmz6xlqmfkplivoyfosxagcpg23rxe.arweave.net/m2IH1JYkKfcXP0DgzPrrgwqnrRV2CulcBhPN_rc_bkY',
13 | },
14 | {
15 | index: 1,
16 | title: 'Kill it anyway',
17 | artiste: 'SteamBeats originals',
18 | plays: '1,324',
19 | songLength: '4:50',
20 | album: 'The Random House',
21 | cover:
22 | 'https://upload.wikimedia.org/wikipedia/commons/3/3b/Javascript_Logo.png',
23 | arweaveLink:
24 | 'https://rnvytwtlgebo5e57kjl5ax3omeow423vmeul25dwmgueyhst.arweave.net/i2uJ2msxAu6Tv1JX0F9uYR1ua_-3VhKL10dmGoTB5TA',
25 | },
26 | {
27 | index: 2,
28 | title: 'Gift of a Guest',
29 | artiste: 'SteamBeats originals',
30 | plays: '45,422',
31 | songLength: '1:33',
32 | album: 'A Regular Album',
33 | cover: 'https://i.ytimg.com/vi/Yaeu73hr_eM/maxresdefault.jpg',
34 | arweaveLink:
35 | 'https://dr5vuvoh5dm7pdnv5i4qo5ezmfmqevs4ytrx5c3wskdby4dy.arweave.net/HHta_Vcfo2feNt-eo5_B3SZYVkCVlzE436LdpKGHHB4',
36 | },
37 | ]
38 |
--------------------------------------------------------------------------------
/hooks/useSpotify.js:
--------------------------------------------------------------------------------
1 | import { TOKEN_PROGRAM_ID } from '@solana/spl-token'
2 | import { useWallet } from '@solana/wallet-adapter-react'
3 | import { SOLANA_HOST } from '../utils/const'
4 | import { getProgramInstance } from '../utils/utils'
5 | const anchor = require('@project-serum/anchor')
6 | const utf8 = anchor.utils.bytes.utf8
7 | const { BN, web3 } = anchor
8 | const { SystemProgram } = web3
9 |
10 | const defaultAccounts = {
11 | tokenProgram: TOKEN_PROGRAM_ID,
12 | clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
13 | systemProgram: SystemProgram.programId,
14 | }
15 |
16 | const useSpotify = (
17 | musicUrl,
18 | title,
19 | setTitle,
20 | setMusicUrl,
21 | setShowUploadMusic,
22 | ) => {
23 | const wallet = useWallet()
24 | const connection = new anchor.web3.Connection(SOLANA_HOST)
25 | const program = getProgramInstance(connection, wallet)
26 |
27 | const getSongs = async () => {
28 | console.log('fetching')
29 |
30 | const songs = await program.account.musicAccount.all()
31 | console.log(songs)
32 | return songs;
33 | }
34 |
35 | const newMusic = async () => {
36 | const randomkey = anchor.web3.Keypair.generate().publicKey;
37 |
38 | let [music_pda] = await anchor.web3.PublicKey.findProgramAddress(
39 | [utf8.encode('music'), randomkey.toBuffer()],
40 | program.programId,
41 | )
42 |
43 | const tx = await program.rpc.createMusic(
44 | title,
45 | musicUrl,
46 | {
47 | accounts: {
48 | music: music_pda,
49 | randomkey: randomkey,
50 | authority: wallet.publicKey,
51 | ...defaultAccounts,
52 | },
53 | },
54 | )
55 |
56 | console.log(tx)
57 |
58 | setTitle('')
59 | setMusicUrl('')
60 | setShowUploadMusic(false)
61 | }
62 |
63 | return { newMusic, getSongs }
64 | }
65 |
66 | export default useSpotify
67 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | env: {
5 | REACT_APP_CLUSTER: process.env.REACT_APP_CLUSTER,
6 | },
7 | images: {
8 | domains: [
9 | 'kajabi-storefronts-production.kajabi-cdn.com',
10 | 'upload.wikimedia.org',
11 | 'i.ytimg.com',
12 | 'angartwork.akamaized.net'
13 | ],
14 | },
15 | }
16 |
17 | module.exports = nextConfig
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bts-spotify-clone-blockchain",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@project-serum/anchor": "^0.22.1",
13 | "@project-serum/common": "^0.0.1-beta.3",
14 | "@project-serum/serum": "^0.13.61",
15 | "@solana/spl-token": "^0.2.0",
16 | "@solana/wallet-adapter-base": "^0.9.5",
17 | "@solana/wallet-adapter-react": "^0.15.4",
18 | "@solana/wallet-adapter-react-ui": "^0.9.6",
19 | "@solana/wallet-adapter-wallets": "^0.15.5",
20 | "@solana/web3.js": "^1.37.0",
21 | "ardrive-core-js": "^1.13.0",
22 | "autoprefixer": "^10.4.4",
23 | "axios": "^0.27.2",
24 | "dotenv": "^16.0.0",
25 | "multiparty": "^4.2.3",
26 | "next": "12.1.0",
27 | "next-connect": "^0.12.2",
28 | "parse-multipart-data": "^1.3.0",
29 | "postcss": "^8.4.12",
30 | "react": "17.0.2",
31 | "react-dom": "17.0.2",
32 | "swr": "^1.3.0",
33 | "tailwindcss": "^3.0.24"
34 | },
35 | "devDependencies": {
36 | "autoprefixer": "^10.4.4",
37 | "eslint": "8.13.0",
38 | "eslint-config-next": "12.1.5",
39 | "postcss": "^8.4.12",
40 | "tailwindcss": "^3.0.24"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import dynamic from 'next/dynamic'
2 | import '../styles/globals.css'
3 | import { SpotifyProvider } from '../context/context'
4 | require('@solana/wallet-adapter-react-ui/styles.css')
5 |
6 | const WalletConnectionProvider = dynamic(
7 | () => import('../context/WalletConnectionProvider'),
8 | {
9 | ssr: false,
10 | },
11 | )
12 |
13 | function MyApp({ Component, pageProps }) {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 |
23 | export default MyApp
24 |
--------------------------------------------------------------------------------
/pages/api/upload_music.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduc
2 | import {
3 | readJWKFile,
4 | arDriveFactory,
5 | wrapFileOrFolder,
6 | EID,
7 | } from 'ardrive-core-js'
8 | const fs = require('fs')
9 | const multipart = require('parse-multipart-data')
10 |
11 | function getMatching(string, regex) {
12 | // Helper function when using non-matching groups
13 | const matches = string.match(regex)
14 | if (!matches || matches.length < 2) {
15 | return null
16 | }
17 | return matches[1]
18 | }
19 |
20 | function dataURLtoFile(dataurl, filename) {
21 | var arr = dataurl.split(','),
22 | mime = arr[0].match(/:(.*?);/)[1],
23 | bstr = atob(arr[1]),
24 | n = bstr.length,
25 | u8arr = new Uint8Array(n)
26 |
27 | while (n--) {
28 | u8arr[n] = bstr.charCodeAt(n)
29 | }
30 |
31 | fs.writeFileSync(filename, Buffer.from(u8arr))
32 | }
33 | export default function handler(req, res) {
34 | dataURLtoFile(req.body.file, req.body.filename)
35 |
36 | // Read wallet from file
37 | const myWallet = readJWKFile('./wallet.json')
38 |
39 | // Construct ArDrive class
40 | const arDrive = arDriveFactory({ wallet: myWallet })
41 |
42 | const wrappedEntity = wrapFileOrFolder(`./${req.body.filename}`)
43 | const destFolderId = EID('77fac565-a8c0-48e0-b64f-e1c0604372d3')
44 |
45 | // Upload a public file to destination folder
46 | arDrive
47 | .uploadAllEntities({
48 | entitiesToUpload: [{ wrappedEntity, destFolderId }],
49 | })
50 | .then(result => res.status(200).json({ result: result }))
51 | .catch(error => res.status(200).json({ error: error }))
52 | }
53 |
54 | export const config = {
55 | api: {
56 | bodyParser: {
57 | sizeLimit: '50mb', // Set desired value here
58 | },
59 | },
60 | }
61 |
--------------------------------------------------------------------------------
/pages/homepage.js:
--------------------------------------------------------------------------------
1 | import Header from '../components/header'
2 | import Nav from '../components/nav'
3 | import Playlist from '../components/playlist'
4 | import PlayerControls from '../components/playerControls'
5 | import Activity from '../components/activity'
6 | import { useState, useEffect } from 'react'
7 | import UploadModal from '../components/UploadModal'
8 | import useSpotify from '../hooks/useSpotify'
9 |
10 | const HomePage = () => {
11 | const [showUploadMusic, setShowUploadMusic] = useState(false)
12 | const [title, setTitle] = useState('')
13 | const [musicUrl, setMusicUrl] = useState('')
14 |
15 | const { newMusic, getSongs } = useSpotify(
16 | musicUrl,
17 | title,
18 | setTitle,
19 | setMusicUrl,
20 | setShowUploadMusic,
21 | )
22 |
23 | const [songs, setSongs] = useState([])
24 |
25 | useEffect(() => {
26 | getSongs().then(songs => {
27 | setSongs(songs)
28 | })
29 | }, [])
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 | {showUploadMusic && (
39 |
47 | )}
48 |
49 |
50 |
51 | )
52 | }
53 |
54 | export default HomePage
55 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { SpotifyContext } from '../context/context'
3 | import Login from '../components/login'
4 |
5 | export default function Home() {
6 | const { updateProgress, updateVolume } = useContext(SpotifyContext)
7 | return (
8 |
9 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/assets/add.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/album.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/spotify-clone-blockchain/afe6e4409227b097d10c65323b8e87b2b7eece4b/public/assets/album.jpg
--------------------------------------------------------------------------------
/public/assets/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/spotify-clone-blockchain/afe6e4409227b097d10c65323b8e87b2b7eece4b/public/assets/avatar.jpg
--------------------------------------------------------------------------------
/public/assets/chevronLeft.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/chevronRight.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/disc.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/friend.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/home.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/more.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/playRounded.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/playlist.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/previous.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/repeat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/shuffle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/speaker.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/spotify-clone-blockchain/afe6e4409227b097d10c65323b8e87b2b7eece4b/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0 2rem;
3 | }
4 |
5 | .main {
6 | min-height: 100vh;
7 | padding: 4rem 0;
8 | flex: 1;
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: center;
12 | align-items: center;
13 | }
14 |
15 | .footer {
16 | display: flex;
17 | flex: 1;
18 | padding: 2rem 0;
19 | border-top: 1px solid #eaeaea;
20 | justify-content: center;
21 | align-items: center;
22 | }
23 |
24 | .footer a {
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 | flex-grow: 1;
29 | }
30 |
31 | .title a {
32 | color: #0070f3;
33 | text-decoration: none;
34 | }
35 |
36 | .title a:hover,
37 | .title a:focus,
38 | .title a:active {
39 | text-decoration: underline;
40 | }
41 |
42 | .title {
43 | margin: 0;
44 | line-height: 1.15;
45 | font-size: 4rem;
46 | }
47 |
48 | .title,
49 | .description {
50 | text-align: center;
51 | }
52 |
53 | .description {
54 | margin: 4rem 0;
55 | line-height: 1.5;
56 | font-size: 1.5rem;
57 | }
58 |
59 | .code {
60 | background: #fafafa;
61 | border-radius: 5px;
62 | padding: 0.75rem;
63 | font-size: 1.1rem;
64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65 | Bitstream Vera Sans Mono, Courier New, monospace;
66 | }
67 |
68 | .grid {
69 | display: flex;
70 | align-items: center;
71 | justify-content: center;
72 | flex-wrap: wrap;
73 | max-width: 800px;
74 | }
75 |
76 | .card {
77 | margin: 1rem;
78 | padding: 1.5rem;
79 | text-align: left;
80 | color: inherit;
81 | text-decoration: none;
82 | border: 1px solid #eaeaea;
83 | border-radius: 10px;
84 | transition: color 0.15s ease, border-color 0.15s ease;
85 | max-width: 300px;
86 | }
87 |
88 | .card:hover,
89 | .card:focus,
90 | .card:active {
91 | color: #0070f3;
92 | border-color: #0070f3;
93 | }
94 |
95 | .card h2 {
96 | margin: 0 0 1rem 0;
97 | font-size: 1.5rem;
98 | }
99 |
100 | .card p {
101 | margin: 0;
102 | font-size: 1.25rem;
103 | line-height: 1.5;
104 | }
105 |
106 | .logo {
107 | height: 1em;
108 | margin-left: 0.5rem;
109 | }
110 |
111 | @media (max-width: 600px) {
112 | .grid {
113 | width: 100%;
114 | flex-direction: column;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/styles/UploadModal.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | position: absolute;
3 | left: 50%;
4 | top: 50%;
5 | transform: translate(-50%, -50%);
6 | z-index: 10;
7 | background-color: black;
8 | min-width: 20rem;
9 | min-height: 20rem;
10 | border-radius: 10px;
11 | padding: 1.2rem;
12 | display: flex;
13 | flex-direction: column;
14 | justify-content: center;
15 | align-items: center;
16 | }
17 |
18 | .modalButtons {
19 | display: flex;
20 | justify-content: space-evenly;
21 | margin-top: 20px;
22 | padding: 10px;
23 | }
24 |
25 | .title {
26 | font-weight: bolder;
27 | font-size: 1.5rem;
28 | margin: 1.5rem 0 3rem 0;
29 | }
30 |
31 | .inputField {
32 | width: 100%;
33 | margin-bottom: 1rem;
34 | }
35 |
36 | .inputTitle {
37 | font-weight: 600;
38 | font-size: 1rem;
39 | margin-bottom: 0.5rem;
40 | }
41 |
42 | .inputContainer {
43 | display: flex;
44 | border: 1px #ecf1f4 solid;
45 | border-radius: 1rem;
46 | padding: 0.6rem 0.4rem;
47 | background-color: #fafcfc;
48 | color: #000;
49 | }
50 |
51 | .input {
52 | flex: 1;
53 | border: none;
54 | outline: none;
55 | background: none;
56 | }
57 |
58 | .button {
59 | color: white;
60 | padding: 5px;
61 | border-radius: 10px;
62 | border: none;
63 | padding: 0.4rem 1rem;
64 | border-radius: 9px;
65 | color: #fff;
66 | cursor: pointer;
67 | transition: 0.2s linear;
68 | width: 8rem;
69 | align-items: center;
70 | justify-content: center;
71 | display: flex;
72 | font-weight: 600;
73 | font-size: 0.9rem;
74 | margin: 0.5rem;
75 | }
76 |
77 | .createButton {
78 | background-color: #4e44ce;
79 | }
80 |
81 | .cancelButton {
82 | border: 2px #fe2d55 solid;
83 | color: #fe2d55;
84 | }
85 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | html,
6 | body {
7 | padding: 0;
8 | margin: 0;
9 | min-height: 100vh;
10 | color: #fff;
11 | background: linear-gradient(to bottom, #BCA0A0, #242424b0, #242424);
12 | }
13 |
14 | a {
15 | color: inherit;
16 | text-decoration: none;
17 | }
18 |
19 | * {
20 | box-sizing: border-box;
21 | }
22 |
23 | input[type=range] {
24 | margin: 0 10px;
25 | height: 6px;
26 | border-radius: 100px;
27 | background: #fff;
28 | appearance: none;
29 | width: 560px;
30 | outline: none;
31 | }
32 |
33 | #volume-range {
34 | width: 200px;
35 | }
36 |
37 | input[type=range]::-moz-range-thumb {
38 | background: #22C55E;
39 | border: none;
40 | }
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | "./pages/**/*.{js,ts,jsx,tsx}",
4 | "./components/**/*.{js,ts,jsx,tsx}",
5 | ],
6 | theme: {
7 | extend: {},
8 | },
9 | plugins: [],
10 | }
11 |
--------------------------------------------------------------------------------
/utils/const.js:
--------------------------------------------------------------------------------
1 | import { clusterApiUrl, PublicKey } from '@solana/web3.js'
2 | import spotify from './spotify.json'
3 |
4 | export const SOLANA_HOST = clusterApiUrl('devnet')
5 |
6 | export const STABLE_POOL_PROGRAM_ID = new PublicKey(
7 | '5wSMdEYxSW7iB3rdE7c8yB3bqBUrVtvggjDdY5viyDLk',
8 | )
9 |
10 | export const STABLE_POOL_IDL = spotify
11 |
--------------------------------------------------------------------------------
/utils/spotify.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "name": "spotify_spl_program",
4 | "instructions": [
5 | {
6 | "name": "acceptPayment",
7 | "accounts": [
8 | {
9 | "name": "payerWallet",
10 | "isMut": true,
11 | "isSigner": false
12 | },
13 | {
14 | "name": "receiver",
15 | "isMut": true,
16 | "isSigner": false
17 | },
18 | {
19 | "name": "authority",
20 | "isMut": true,
21 | "isSigner": true
22 | },
23 | {
24 | "name": "systemProgram",
25 | "isMut": false,
26 | "isSigner": false
27 | },
28 | {
29 | "name": "tokenProgram",
30 | "isMut": false,
31 | "isSigner": false
32 | },
33 | {
34 | "name": "clock",
35 | "isMut": false,
36 | "isSigner": false
37 | }
38 | ],
39 | "args": []
40 | },
41 | {
42 | "name": "createMusic",
43 | "accounts": [
44 | {
45 | "name": "music",
46 | "isMut": true,
47 | "isSigner": false
48 | },
49 | {
50 | "name": "randomkey",
51 | "isMut": true,
52 | "isSigner": false
53 | },
54 | {
55 | "name": "authority",
56 | "isMut": true,
57 | "isSigner": true
58 | },
59 | {
60 | "name": "systemProgram",
61 | "isMut": false,
62 | "isSigner": false
63 | },
64 | {
65 | "name": "tokenProgram",
66 | "isMut": false,
67 | "isSigner": false
68 | },
69 | {
70 | "name": "clock",
71 | "isMut": false,
72 | "isSigner": false
73 | }
74 | ],
75 | "args": [
76 | {
77 | "name": "title",
78 | "type": "string"
79 | },
80 | {
81 | "name": "musicUrl",
82 | "type": "string"
83 | }
84 | ]
85 | }
86 | ],
87 | "accounts": [
88 | {
89 | "name": "PayerAccount",
90 | "type": {
91 | "kind": "struct",
92 | "fields": [
93 | {
94 | "name": "wallet",
95 | "type": "publicKey"
96 | }
97 | ]
98 | }
99 | },
100 | {
101 | "name": "MusicAccount",
102 | "type": {
103 | "kind": "struct",
104 | "fields": [
105 | {
106 | "name": "authority",
107 | "type": "publicKey"
108 | },
109 | {
110 | "name": "title",
111 | "type": "string"
112 | },
113 | {
114 | "name": "musicUrl",
115 | "type": "string"
116 | }
117 | ]
118 | }
119 | }
120 | ],
121 | "metadata": {
122 | "address": "5wSMdEYxSW7iB3rdE7c8yB3bqBUrVtvggjDdY5viyDLk"
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/utils/utils.js:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { Connection } from '@solana/web3.js'
3 | import { WalletNotConnectedError } from '@solana/wallet-adapter-base'
4 | import { STABLE_POOL_IDL, STABLE_POOL_PROGRAM_ID } from './const'
5 |
6 | // This command makes an Lottery
7 | export function getProgramInstance(connection, wallet) {
8 | if (!wallet.publicKey) throw new WalletNotConnectedError()
9 |
10 | const provider = new anchor.Provider(
11 | connection,
12 | wallet,
13 | anchor.Provider.defaultOptions(),
14 | )
15 | // Read the generated IDL.
16 | const idl = STABLE_POOL_IDL
17 |
18 | // Address of the deployed program.
19 | const programId = STABLE_POOL_PROGRAM_ID
20 |
21 | // Generate the program client from IDL.
22 | const program = new anchor.Program(idl, programId, provider)
23 |
24 | return program
25 | }
26 |
--------------------------------------------------------------------------------
/wallet.json:
--------------------------------------------------------------------------------
1 | {
2 | "wallet": "insert your wallet config file here"
3 | }
4 |
--------------------------------------------------------------------------------