Skip to content

Commit f8c1367

Browse files
authored
Merge pull request #883 from streamethorg/speakerPage
Add Zora Success Modal
2 parents 7d30198 + d539dc9 commit f8c1367

File tree

9 files changed

+97
-128
lines changed

9 files changed

+97
-128
lines changed

packages/app/app/speaker/[session]/components/ZoraUploadButton.tsx

Lines changed: 87 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
'use client';
22

33
import { track } from '@vercel/analytics';
4-
import { useState, useCallback } from 'react';
4+
import { useState, useCallback, useEffect } from 'react';
55
import {
66
useAccount,
77
usePublicClient,
88
useWriteContract,
99
useSwitchChain,
10+
useWaitForTransactionReceipt,
1011
} from 'wagmi';
1112
import {
1213
createCreatorClient,
@@ -23,6 +24,10 @@ import { createThirdwebClient } from 'thirdweb';
2324
import { createStateAction } from '@/lib/actions/state';
2425
import { StateType } from 'streameth-new-server/src/interfaces/state.interface';
2526
import useGenerateThumbnail from '@/lib/hooks/useGenerateThumbnail';
27+
import { Dialog, DialogContent, DialogFooter } from '@/components/ui/dialog';
28+
import { DialogTitle } from '@radix-ui/react-dialog';
29+
import Link from 'next/link';
30+
import CopyText from '@/components/misc/CopyText';
2631

2732
const BASE_CHAIN_ID = 8453;
2833

@@ -67,15 +72,44 @@ const ZoraUploadButton = ({
6772
variant = 'primary',
6873
}: {
6974
session: IExtendedSession;
70-
state: IExtendedState | null;
75+
state: IExtendedState[];
7176
variant?: 'primary' | 'outline';
7277
}) => {
73-
const thumbnail = useGenerateThumbnail({ session: session });
78+
const thumbnail = useGenerateThumbnail({ session });
7479
const [isUploading, setIsUploading] = useState(false);
7580
const publicClient = usePublicClient({ chainId: BASE_CHAIN_ID });
7681
const { address, isConnected } = useAccount();
77-
const { writeContract } = useWriteContract();
82+
const { data: hash, writeContract, isError } = useWriteContract();
7883
const { switchChain } = useSwitchChain();
84+
const [isPublishedModal, setIsPublishedModal] = useState(false);
85+
const [zoraContractAddress, setZoraContractAddress] = useState('');
86+
const { isSuccess } = useWaitForTransactionReceipt({
87+
hash,
88+
});
89+
90+
const updateState = async () => {
91+
await createStateAction({
92+
state: {
93+
sessionId: session._id,
94+
type: 'nft' as StateType,
95+
sessionSlug: session.slug,
96+
organizationId: session.organizationId,
97+
},
98+
});
99+
};
100+
useEffect(() => {
101+
if (isSuccess) {
102+
updateState();
103+
setIsPublishedModal(true);
104+
setIsUploading(false);
105+
toast.success('Video successfully uploaded to Zora marketplace on Base');
106+
}
107+
108+
if (isError) {
109+
setIsUploading(false);
110+
toast.error('Creation failed');
111+
}
112+
}, [isSuccess, isError]);
79113

80114
const uploadToZora = useCallback(async () => {
81115
if (!publicClient || !address || !session.assetId) return;
@@ -92,7 +126,7 @@ const ZoraUploadButton = ({
92126
mediaUrl: downloadUrl,
93127
thumbnailUrl: coverImageUri,
94128
});
95-
129+
console.log('coverImageUri', coverImageUri, 'thumbnailUrl', thumbnail);
96130
const tokenMetadataUri = await uploadToIPFS(tokenMetadata);
97131

98132
if (BASE_CHAIN_ID !== (await publicClient.getChainId())) {
@@ -124,45 +158,68 @@ const ZoraUploadButton = ({
124158

125159
writeContract(parameters);
126160

127-
//await createStateAction({
128-
// state: {
129-
// sessionId: session._id,
130-
// type: StateType.zoraNft,
131-
// sessionSlug: session.slug,
132-
// organizationId: session.organizationId,
133-
// },
134-
//});
161+
setZoraContractAddress(contractAddress);
135162
console.log('Upload successful. Contract address:', contractAddress);
136-
toast.success('Video successfully uploaded to Zora marketplace on Base');
137163
} catch (error) {
138164
console.error('Error uploading to Zora:', error);
139165
toast.error('Failed to upload video to Zora marketplace');
140166
} finally {
141-
setIsUploading(false);
142167
}
143168
}, [publicClient, address, session, writeContract, switchChain]);
144169

145170
if (!isConnected) {
146171
return <ConnectWalletButton />;
147172
}
148173

149-
const isDisabled = isUploading || !state;
174+
const isDisabled = isUploading || state?.length > 0;
150175

151176
return (
152-
<Button
153-
onClick={() => {
154-
track('Upload to Zora', { location: 'Speaker Page' });
155-
uploadToZora();
156-
}}
157-
disabled={isDisabled}
158-
variant={variant}
159-
>
160-
{isUploading
161-
? 'Uploading...'
162-
: state
163-
? 'Upload to Zora'
164-
: 'Already Uploaded'}
165-
</Button>
177+
<>
178+
<Button
179+
onClick={() => {
180+
track('Upload to Zora', { location: 'Speaker Page' });
181+
uploadToZora();
182+
}}
183+
disabled={isDisabled}
184+
variant={variant}
185+
>
186+
{isUploading
187+
? 'Uploading...'
188+
: !state[0]
189+
? 'Upload to Zora'
190+
: 'Already Uploaded'}
191+
</Button>
192+
<Dialog open={isPublishedModal} onOpenChange={setIsPublishedModal}>
193+
<DialogContent className="lg:min-w-[600px]">
194+
<DialogTitle className="font-bold">
195+
Upload to Zora Completed
196+
</DialogTitle>
197+
<p>Video successfully published to Zora</p>
198+
<CopyText
199+
width="100%"
200+
label="Contract Address"
201+
text={zoraContractAddress}
202+
/>
203+
204+
<Link
205+
target="blank"
206+
rel="noopener noreferrer"
207+
href={`https://zora.co/collect/base:${zoraContractAddress}/1`}
208+
className="underline"
209+
>
210+
View on Zora
211+
</Link>
212+
<DialogFooter>
213+
<Button
214+
onClick={() => setIsPublishedModal(false)}
215+
variant={'outline'}
216+
>
217+
Close
218+
</Button>
219+
</DialogFooter>
220+
</DialogContent>
221+
</Dialog>
222+
</>
166223
);
167224
};
168225

packages/app/app/speaker/[session]/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ const SessionPage = async ({
5858
if (!videoUrl) return notFound();
5959

6060
const state = (await fetchAllStates({ sessionId: session._id })).filter(
61-
(state) => state.type === StateType.zoraNft
62-
) as unknown as IExtendedState;
61+
(state) => state.type === StateType.nft
62+
) as IExtendedState[];
6363

6464
const thumbnail = await generateThumbnailAction(session!);
6565
const youtubeData = cookies().get('youtube_publish')?.value;

packages/app/components/sessions/CollectVideButton.tsx

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client';
2-
import React, { useEffect, useState, useCallback } from 'react';
2+
import React, { useEffect, useState } from 'react';
33
import { Button } from '../ui/button';
44
import { IExtendedNftCollections, IExtendedSession } from '@/lib/types';
55
import {
@@ -9,7 +9,6 @@ import {
99
useSwitchChain,
1010
useWaitForTransactionReceipt,
1111
useWriteContract,
12-
usePublicClient,
1312
} from 'wagmi';
1413
import { VideoNFTAbi, contractChainID } from '@/lib/contract';
1514
import { toast } from 'sonner';
@@ -19,29 +18,22 @@ import { CheckCircle2, Loader2 } from 'lucide-react';
1918
import { type BaseError } from 'wagmi';
2019
import TransactionHash from '@/app/studio/[organization]/nfts/create/components/TransactionHash';
2120
import { ConnectWalletButton } from '../misc/ConnectWalletButton';
22-
import { createCreatorClient } from '@zoralabs/protocol-sdk';
23-
import { zora } from 'viem/chains';
2421

2522
const CollectVideButton = ({
2623
video,
2724
nftCollection,
2825
variant = 'primary',
2926
all = false,
30-
standalone = false,
3127
}: {
3228
video?: IExtendedSession;
3329
nftCollection: IExtendedNftCollections | null;
3430
variant?: 'primary' | 'outline';
3531
all?: boolean;
36-
standalone?: boolean;
3732
}) => {
3833
const account = useAccount();
3934
const chain = useChainId();
40-
const publicClient = usePublicClient();
4135
const [isOpen, setIsOpen] = useState(false);
4236
const [mintError, setMintError] = useState('');
43-
const [isUploading, setIsUploading] = useState(false);
44-
const [showUploadButton, setShowUploadButton] = useState(false);
4537

4638
const videoNFTContract = {
4739
address: nftCollection?.contractAddress as `0x${string}`,
@@ -90,7 +82,6 @@ const CollectVideButton = ({
9082
if (isSuccess) {
9183
setIsOpen(false);
9284
toast.success('NFT of the video successfully minted to your wallet');
93-
setShowUploadButton(true);
9485
}
9586
if (isError) {
9687
toast.error('NFT of the video failed to mint. Please try again');
@@ -114,7 +105,7 @@ const CollectVideButton = ({
114105
),
115106
});
116107
} else {
117-
setMintError('An error occurred, try again later please');
108+
setMintError('An error occurred, try again later');
118109
}
119110
};
120111

@@ -138,73 +129,6 @@ const CollectVideButton = ({
138129
}
139130
};
140131

141-
const uploadToZora = useCallback(async () => {
142-
if (!publicClient || !account.address) return;
143-
144-
setIsUploading(true);
145-
try {
146-
const creatorClient = createCreatorClient({
147-
chainId: zora.id,
148-
publicClient,
149-
});
150-
151-
const { parameters, contractAddress } = await creatorClient.create1155({
152-
contract: {
153-
name: video?.name || 'Video NFT',
154-
uri: 'ipfs://DUMMY/contract.json', // Replace with actual contract metadata URI
155-
},
156-
token: {
157-
tokenMetadataURI: 'ipfs://DUMMY/token.json', // Replace with actual token metadata URI
158-
salesConfig: {
159-
erc20Name: video?.name || 'Video Token',
160-
erc20Symbol: 'VNFT',
161-
},
162-
},
163-
account: account.address,
164-
});
165-
166-
writeContract(parameters);
167-
168-
toast.success('Session successfully uploaded to Zora marketplace');
169-
setShowUploadButton(false);
170-
} catch (error) {
171-
console.error('Error uploading to Zora:', error);
172-
toast.error('Failed to upload session to Zora marketplace');
173-
} finally {
174-
setIsUploading(false);
175-
}
176-
}, [publicClient, account.address, video, writeContract]);
177-
178-
useEffect(() => {
179-
if (account.isConnected && standalone) {
180-
toast.success('Wallet connected successfully');
181-
setShowUploadButton(true);
182-
}
183-
}, [account.isConnected, standalone]);
184-
185-
if (standalone) {
186-
return (
187-
<div>
188-
{!account.isConnected ? (
189-
<ConnectWalletButton />
190-
) : showUploadButton ? (
191-
<Button
192-
onClick={uploadToZora}
193-
loading={isUploading}
194-
variant={variant}
195-
className="w-full md:w-36"
196-
>
197-
Upload to Zora
198-
</Button>
199-
) : (
200-
<Button disabled variant="outline">
201-
Uploaded to Zora
202-
</Button>
203-
)}
204-
</div>
205-
);
206-
}
207-
208132
return hasMintedSession ? (
209133
<Button disabled variant={'outline'}>
210134
Session Collected
@@ -213,15 +137,6 @@ const CollectVideButton = ({
213137
<div>
214138
{!account.address ? (
215139
<ConnectWalletButton />
216-
) : showUploadButton ? (
217-
<Button
218-
onClick={uploadToZora}
219-
loading={isUploading}
220-
variant={variant}
221-
className="w-full md:w-36"
222-
>
223-
Upload to Zora
224-
</Button>
225140
) : (
226141
<Button
227142
loading={isMintingNftPending || IsSwitchingChain}

packages/app/components/sessions/SessionInfoBox.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ const DesktopButtons = ({
3737
return (
3838
<>
3939
{video?.nftCollections?.[0] && (
40-
<CollectVideButton
41-
video={video}
42-
nftCollection={nftCollection}
43-
standalone={true}
44-
/>
40+
<CollectVideButton video={video} nftCollection={nftCollection} />
4541
)}
4642
<div className="flex flex-row space-x-2">
4743
<ShareButton shareFor="video" />

packages/app/lib/actions/state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const createStateAction = async ({ state }: { state: IState }) => {
99
if (!authToken) {
1010
throw new Error('No user session found');
1111
}
12-
console.log('Creating state:', state);
12+
1313
const response = await createState({
1414
state,
1515
authToken,

packages/app/lib/services/stateService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const createState = async ({
1414
state: IState;
1515
authToken: string;
1616
}): Promise<IState> => {
17+
console.log('fe state', state);
1718
try {
1819
const response = await fetch(`${apiUrl()}/states`, {
1920
method: 'POST',

packages/server/src/interfaces/state.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export enum StateStatus {
1414
}
1515

1616
export enum StateType {
17-
zoraNft = 'zora',
17+
nft = 'nft',
1818
event = 'event',
1919
video = 'video',
2020
}

packages/server/src/routes/routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ const models: TsoaRoute.Models = {
259259
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
260260
"StateType": {
261261
"dataType": "refEnum",
262-
"enums": ["zora","event","video"],
262+
"enums": ["nft","event","video"],
263263
},
264264
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
265265
"IState": {

packages/server/src/swagger/swagger.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@
424424
"type": "string"
425425
},
426426
"StateType": {
427-
"enum": ["zora", "event", "video"],
427+
"enum": ["nft", "event", "video"],
428428
"type": "string"
429429
},
430430
"IState": {

0 commit comments

Comments
 (0)