Skip to content

Commit 09f8a23

Browse files
feat: Add Releases page (#62)
1 parent 94734e7 commit 09f8a23

15 files changed

Lines changed: 444 additions & 71 deletions

File tree

apps/dapp/src/app/(user)/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export default function ProfileLayout({
77
children: React.ReactNode;
88
}) {
99
return (
10-
<>
10+
<div className="overflow-y-scroll h-[100vh]">
1111
<NavbarDashboard />
1212
<ProfileContextProvider>{children}</ProfileContextProvider>
13-
</>
13+
</div>
1414
);
1515
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import ReleasesList from '@/components/User/Releases/ReleasesList';
2+
3+
export default function ReleasesPage() {
4+
return (
5+
<div className="">
6+
<ReleasesList />
7+
</div>
8+
);
9+
}
Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1+
import { getEvmApi } from '@/utils/evmApi';
12
import { getChainInfo } from '@/utils/getChainInfo';
2-
import { EvmAbiItem, EvmChain } from '@moralisweb3/common-evm-utils';
3+
import {
4+
EvmAbiItem,
5+
EvmAddressInput,
6+
EvmChain,
7+
} from '@moralisweb3/common-evm-utils';
38
import { Addresses } from 'drop-sdk';
4-
import Moralis from 'moralis';
5-
6-
const apiKey = process.env.NEXT_PUBLIC_MORALIS_API_KEY || '';
7-
8-
Moralis.start({
9-
apiKey,
10-
});
119

1210
interface Params {
1311
params: { address: string };
@@ -28,12 +26,14 @@ interface ProfileChangeForResponseJson {
2826
}
2927

3028
export async function GET(_request: Request, { params }: Params) {
29+
const evmApi = await getEvmApi();
3130
const chainInfo = getChainInfo();
3231

3332
const chain = chainInfo.isTestnet ? EvmChain.MUMBAI : EvmChain.POLYGON;
3433

35-
const profileAddress =
36-
chain === EvmChain.MUMBAI ? Addresses.Profile.mumbai : '';
34+
const profileAddress = chainInfo.isTestnet
35+
? (Addresses.Profile.mumbai as EvmAddressInput)
36+
: '';
3737

3838
const topic =
3939
'0x851e62abe600e90c07bdd93b1db315b32f32d4845f6cc42b28e6f1acb458eaee';
@@ -58,30 +58,35 @@ export async function GET(_request: Request, { params }: Params) {
5858
anonymous: false,
5959
};
6060

61-
const response = await Moralis.EvmApi.events.getContractEvents({
62-
address: profileAddress,
63-
chain,
64-
topic,
65-
abi,
66-
});
67-
68-
const responses: ProfileChangeForResponseJson[] = response.toJSON()
69-
.result as ProfileChangeForResponseJson[];
70-
71-
const events = responses.map((event) => {
72-
return event.data;
73-
});
74-
75-
const filteredEvents = events.filter((event) => {
76-
return event.caller.toLowerCase() === params.address.toLowerCase();
77-
});
78-
79-
const contractAddresses = filteredEvents.map((event) => {
80-
return event.kontract;
81-
});
82-
83-
const uniqueContractAddressesSet = new Set(contractAddresses);
84-
const uniqueContractAddresses = Array.from(uniqueContractAddressesSet);
85-
86-
return new Response(JSON.stringify(uniqueContractAddresses));
61+
try {
62+
const response = await evmApi.events.getContractEvents({
63+
address: profileAddress,
64+
chain,
65+
topic,
66+
abi,
67+
});
68+
69+
const responses: ProfileChangeForResponseJson[] = response.toJSON()
70+
.result as ProfileChangeForResponseJson[];
71+
72+
const events = responses.map((event) => {
73+
return event.data;
74+
});
75+
76+
const filteredEvents = events.filter((event) => {
77+
return event.caller.toLowerCase() === params.address.toLowerCase();
78+
});
79+
80+
const contractAddresses = filteredEvents.map((event) => {
81+
return event.kontract;
82+
});
83+
84+
const uniqueContractAddressesSet = new Set(contractAddresses);
85+
const uniqueContractAddresses = Array.from(uniqueContractAddressesSet);
86+
87+
return new Response(JSON.stringify(uniqueContractAddresses));
88+
} catch (e) {
89+
console.error(e);
90+
return new Response(JSON.stringify(e), { status: 400 });
91+
}
8792
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { getEvmApi } from '@/utils/evmApi';
2+
import { getChainInfo } from '@/utils/getChainInfo';
3+
import { EvmAddressInput, EvmChain } from '@moralisweb3/common-evm-utils';
4+
import { Addresses } from 'drop-sdk';
5+
6+
interface Params {
7+
params: { address: string };
8+
}
9+
10+
export async function GET(_request: Request, { params }: Params) {
11+
const evmApi = await getEvmApi();
12+
const chainInfo = getChainInfo();
13+
14+
const chain = chainInfo.isTestnet ? EvmChain.MUMBAI : EvmChain.POLYGON;
15+
16+
const releasesAddress = chainInfo.isTestnet
17+
? (Addresses.Examples.OpenReleases.mumbai as EvmAddressInput)
18+
: '';
19+
20+
try {
21+
const response = await evmApi.nft.getWalletNFTs({
22+
chain,
23+
format: 'decimal',
24+
tokenAddresses: [releasesAddress],
25+
mediaItems: false,
26+
address: params.address as EvmAddressInput,
27+
});
28+
29+
const releases = response.result.map((release) => {
30+
return {
31+
amountOwned: release.amount,
32+
releaseId: release.tokenId,
33+
releaseUri: release.tokenUri,
34+
};
35+
});
36+
return new Response(JSON.stringify(releases));
37+
} catch (e) {
38+
console.error(e);
39+
return new Response(JSON.stringify(e), { status: 400 });
40+
}
41+
}

apps/dapp/src/app/api/registered-tracks/[address]/route.ts

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import { getChainInfo } from '@/utils';
2+
import { getEvmApi } from '@/utils/evmApi';
23
import {
34
EvmAbiItem,
45
EvmAddressInput,
56
EvmChain,
67
} from '@moralisweb3/common-evm-utils';
78
import { Addresses } from 'drop-sdk';
8-
import Moralis from 'moralis';
9-
10-
const apiKey = process.env.NEXT_PUBLIC_MORALIS_API_KEY || '';
11-
12-
Moralis.start({
13-
apiKey,
14-
});
159

1610
interface Params {
1711
params: { address: string };
@@ -32,12 +26,15 @@ interface TrackRegisteredResponseJson {
3226
data: TrackRegistered;
3327
}
3428
export async function GET(_request: Request, { params }: Params) {
35-
const address = Addresses.Examples.Catalog.mumbai as EvmAddressInput;
36-
29+
const evmApi = await getEvmApi();
3730
const chainInfo = getChainInfo();
3831

3932
const chain = chainInfo.isTestnet ? EvmChain.MUMBAI : EvmChain.POLYGON;
4033

34+
const catalogAddress = chainInfo.isTestnet
35+
? (Addresses.Examples.Catalog.mumbai as EvmAddressInput)
36+
: '';
37+
4138
const topic =
4239
'0x2a8ca0ae0939f472e8138500e93789b904cfd5c773233ce70a80838abe7b6b29';
4340

@@ -67,30 +64,35 @@ export async function GET(_request: Request, { params }: Params) {
6764
type: 'event',
6865
};
6966

70-
const response = await Moralis.EvmApi.events.getContractEvents({
71-
address,
72-
chain,
73-
topic,
74-
abi,
75-
});
67+
try {
68+
const response = await evmApi.events.getContractEvents({
69+
address: catalogAddress,
70+
chain,
71+
topic,
72+
abi,
73+
});
7674

77-
const responses: TrackRegisteredResponseJson[] = response.toJSON()
78-
.result as TrackRegisteredResponseJson[];
75+
const responses: TrackRegisteredResponseJson[] = response.toJSON()
76+
.result as TrackRegisteredResponseJson[];
7977

80-
const events = responses.map((event) => {
81-
return event.data;
82-
});
78+
const events = responses.map((event) => {
79+
return event.data;
80+
});
8381

84-
const filteredEvents = events.filter((event) => {
85-
return event.trackOwner.toLowerCase() === params.address.toLowerCase();
86-
});
82+
const filteredEvents = events.filter((event) => {
83+
return event.trackOwner.toLowerCase() === params.address.toLowerCase();
84+
});
8785

88-
const registeredTracks = filteredEvents.map((event) => {
89-
return {
90-
trackUri: event.trackRegistrationHash,
91-
trackId: event.trackId,
92-
};
93-
});
86+
const registeredTracks = filteredEvents.map((event) => {
87+
return {
88+
trackUri: event.trackRegistrationHash,
89+
trackId: event.trackId,
90+
};
91+
});
9492

95-
return new Response(JSON.stringify(registeredTracks));
93+
return new Response(JSON.stringify(registeredTracks));
94+
} catch (e) {
95+
console.error(e);
96+
return new Response(JSON.stringify(e), { status: 400 });
97+
}
9698
}

apps/dapp/src/components/User/Dashboard/DashboardLinks.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default function DashboardLinks() {
99
<div className=" flex flex-row justify-center gap-6 font-extrabold text-base italic">
1010
<Link href={'/dashboard'}>Dashboard</Link>
1111
<Link href={'/tracks'}>Tracks</Link>
12+
<Link href={'/releases'}>Releases</Link>
1213
</div>
1314
</div>
1415
);
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { useGetTrackMetadata } from '@/hooks/useGetTrackMetadata';
2+
import { ReleaseMetadata } from '@/types';
3+
import { PauseButton, PlayButton } from '@/ui/Icons';
4+
import { UiSpinner } from '@/ui/UiSpinner';
5+
import Image from 'next/image';
6+
import { useRef, useState } from 'react';
7+
8+
interface ReleaseMetadataProps {
9+
releaseMetadata: ReleaseMetadata;
10+
}
11+
12+
export default function ReleaseInfo({ releaseMetadata }: ReleaseMetadataProps) {
13+
return (
14+
<div className="flex flex-col items-center justify-center w-full">
15+
<div className="flex flex-col w-full justify-between p-6 font-bold italic gap-2">
16+
<h3 className="text-2xl ">{releaseMetadata.name}</h3>
17+
<div className="flex flex-row gap-2">
18+
<h3>Description: </h3>
19+
<h3 className="font-normal">{releaseMetadata.description} </h3>
20+
</div>
21+
<div className="flex flex-row gap-2">
22+
<h3>Genre: </h3>
23+
<h3 className="font-normal">
24+
{releaseMetadata.attributes[1].value}{' '}
25+
</h3>
26+
</div>
27+
</div>
28+
<div className="w-full italic font-bold">
29+
<h3 className="text-lg pl-6">Track list</h3>
30+
{releaseMetadata.attributes[0].value.map((trackUri) => (
31+
<Track key={trackUri} trackUri={trackUri} />
32+
))}
33+
</div>
34+
</div>
35+
);
36+
}
37+
38+
interface TrackProps {
39+
trackUri: string;
40+
}
41+
42+
function Track({ trackUri }: TrackProps) {
43+
const { trackMetadata, isLoading, error } = useGetTrackMetadata(trackUri);
44+
45+
const storageUrl = process.env.NEXT_PUBLIC_STORAGE_URL;
46+
47+
const [isPlaying, setIsPlaying] = useState(false);
48+
const audioRef = useRef<HTMLAudioElement>(null);
49+
50+
const togglePlayPause = () => {
51+
if (audioRef.current) {
52+
if (audioRef.current.paused) {
53+
audioRef.current.play();
54+
setIsPlaying(true);
55+
} else {
56+
audioRef.current.pause();
57+
setIsPlaying(false);
58+
}
59+
}
60+
};
61+
62+
if (isLoading) {
63+
return (
64+
<div className="flex flex-row justify-center items-center gap-24 p-4 border-b-2">
65+
<UiSpinner />
66+
</div>
67+
);
68+
}
69+
70+
return (
71+
<div>
72+
{error ? (
73+
<h3>{error.message}</h3>
74+
) : (
75+
<div className="flex flex-row justify-between items-center mx-6 py-4 border-b-2 ">
76+
<div className="flex flex-row gap-2 items-center">
77+
<div className="relative h-12 w-12 shadow-md">
78+
<Image
79+
src={`${storageUrl}${trackMetadata.image}`}
80+
alt={trackMetadata.name}
81+
fill
82+
/>
83+
</div>
84+
<div className="flex flex-col items-start justify-start">
85+
<h3>{trackMetadata.name}</h3>
86+
87+
{/* biome-ignore lint/a11y/useMediaCaption: <explanation> */}
88+
<audio
89+
src={`${storageUrl}${trackMetadata.animation_url}`}
90+
ref={audioRef}
91+
/>
92+
{!isPlaying ? (
93+
<PlayButton
94+
className="rounded-full h-5 w-5 bg-gray-400 flex flex-col items-center justify-center"
95+
className_Icon="h-3 w-3 text-white font-semibold"
96+
onClick={togglePlayPause}
97+
/>
98+
) : (
99+
<PauseButton
100+
className="rounded-full h-5 w-5 bg-gray-400 flex flex-col items-center justify-center"
101+
className_Icon="h-3 w-3 text-white font-semibold"
102+
onClick={togglePlayPause}
103+
/>
104+
)}
105+
</div>
106+
</div>
107+
</div>
108+
)}
109+
</div>
110+
);
111+
}

0 commit comments

Comments
 (0)