Skip to content

Commit 48181a4

Browse files
authored
Merge branch 'main' into greg/cnct-2422-throw-if-sharded-migration-fails
2 parents 5a5673e + 6bd9683 commit 48181a4

File tree

86 files changed

+3160
-1715
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3160
-1715
lines changed

.changeset/gold-pumpkins-poke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@thirdweb-dev/service-utils": patch
3+
---
4+
5+
chore: Allow optional includeUsage (default: true)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"use server";
2+
3+
import { getAuthToken } from "../../app/api/lib/getAuthToken";
4+
import { API_SERVER_URL } from "../constants/env";
5+
6+
export async function confirmEmailWithOTP(otp: string) {
7+
const token = await getAuthToken();
8+
9+
if (!token) {
10+
return {
11+
errorMessage: "You are not authorized to perform this action",
12+
};
13+
}
14+
15+
const res = await fetch(`${API_SERVER_URL}/v1/account/confirmEmail`, {
16+
method: "PUT",
17+
headers: {
18+
"Content-Type": "application/json",
19+
Authorization: `Bearer ${token}`,
20+
},
21+
body: JSON.stringify({
22+
confirmationToken: otp,
23+
}),
24+
});
25+
26+
if (!res.ok) {
27+
const json = await res.json();
28+
29+
if (json.error) {
30+
return {
31+
errorMessage: json.error.message,
32+
};
33+
}
34+
35+
return {
36+
errorMessage: "Failed to confirm email",
37+
};
38+
}
39+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use server";
2+
3+
import { getRawAccount } from "../../app/account/settings/getAccount";
4+
5+
export async function getRawAccountAction() {
6+
return getRawAccount();
7+
}

apps/dashboard/src/@/actions/joinWaitlist.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export async function joinTeamWaitlist(options: {
1212
const token = await getAuthToken();
1313

1414
if (!token) {
15-
throw new Error("No Auth token");
15+
return {
16+
errorMessage: "You are not authorized to perform this action",
17+
};
1618
}
1719

1820
const res = await fetch(`${API_SERVER_URL}/v1/teams/${teamSlug}/waitlist`, {
@@ -27,8 +29,12 @@ export async function joinTeamWaitlist(options: {
2729
});
2830

2931
if (!res.ok) {
30-
throw new Error("Failed to join waitlist");
32+
return {
33+
errorMessage: "Failed to join waitlist",
34+
};
3135
}
3236

33-
return true;
37+
return {
38+
success: true,
39+
};
3440
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use server";
2+
import { getAuthToken } from "../../app/api/lib/getAuthToken";
3+
import { API_SERVER_URL } from "../constants/env";
4+
5+
export async function updateAccount(values: {
6+
name?: string;
7+
email?: string;
8+
}) {
9+
const token = await getAuthToken();
10+
11+
if (!token) {
12+
throw new Error("No Auth token");
13+
}
14+
15+
const res = await fetch(`${API_SERVER_URL}/v1/account`, {
16+
method: "PUT",
17+
headers: {
18+
"Content-Type": "application/json",
19+
Authorization: `Bearer ${token}`,
20+
},
21+
body: JSON.stringify(values),
22+
});
23+
24+
if (!res.ok) {
25+
const json = await res.json();
26+
27+
if (json.error) {
28+
return {
29+
errorMessage: json.error.message,
30+
};
31+
}
32+
33+
return {
34+
errorMessage: "Failed To Update Account",
35+
};
36+
}
37+
}

apps/dashboard/src/@/api/team.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import "server-only";
2-
import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie";
32
import { API_SERVER_URL } from "@/constants/env";
4-
import { cookies } from "next/headers";
53
import { getAuthToken } from "../../app/api/lib/getAuthToken";
64

75
export type Team = {
@@ -38,14 +36,9 @@ export async function getTeamBySlug(slug: string) {
3836
}
3937

4038
export async function getTeams() {
41-
const cookiesManager = await cookies();
42-
const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
43-
const token = activeAccount
44-
? cookiesManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value
45-
: null;
46-
39+
const token = await getAuthToken();
4740
if (!token) {
48-
return [];
41+
return null;
4942
}
5043

5144
const teamsRes = await fetch(`${API_SERVER_URL}/v1/teams`, {
@@ -56,7 +49,7 @@ export async function getTeams() {
5649
if (teamsRes.ok) {
5750
return (await teamsRes.json())?.result as Team[];
5851
}
59-
return [];
52+
return null;
6053
}
6154

6255
type TeamNebulWaitList = {

apps/dashboard/src/@3rdweb-sdk/react/components/connect-wallet/index.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
44
import { Button } from "@/components/ui/button";
55
import { useThirdwebClient } from "@/constants/thirdweb.client";
66
import { useStore } from "@/lib/reactive";
7+
import { cn } from "@/lib/utils";
78
import { getSDKTheme } from "app/components/sdk-component-theme";
89
import { CustomChainRenderer } from "components/selects/CustomChainRenderer";
910
import { mapV4ChainToV5Chain } from "contexts/map-chains";
@@ -35,6 +36,7 @@ export const CustomConnectWallet = (props: {
3536
connectButtonClassName?: string;
3637
signInLinkButtonClassName?: string;
3738
detailsButtonClassName?: string;
39+
loadingButtonClassName?: string;
3840
chain?: Chain;
3941
}) => {
4042
const thirdwebClient = useThirdwebClient();
@@ -123,7 +125,12 @@ export const CustomConnectWallet = (props: {
123125
if (isPending) {
124126
return (
125127
<>
126-
<div className="flex h-[48px] w-[144px] items-center justify-center rounded-lg border border-border bg-muted">
128+
<div
129+
className={cn(
130+
"flex h-[48px] w-[144px] items-center justify-center rounded-lg border border-border bg-muted",
131+
props.loadingButtonClassName,
132+
)}
133+
>
127134
<Spinner className="size-4" />
128135
</div>
129136
</>

apps/dashboard/src/@3rdweb-sdk/react/hooks/useApi.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { UserOpStats } from "@/api/analytics";
2+
import type { Team } from "@/api/team";
23
import {
34
type Query,
45
useMutation,
@@ -29,9 +30,6 @@ export const accountPlan = {
2930
enterprise: "enterprise",
3031
} as const;
3132

32-
type AccountStatus = (typeof accountStatus)[keyof typeof accountStatus];
33-
type AccountPlan = (typeof accountPlan)[keyof typeof accountPlan];
34-
3533
export type AuthorizedWallet = {
3634
id: string;
3735
accountId: string;
@@ -47,30 +45,17 @@ export type Account = {
4745
id: string;
4846
isStaff: boolean;
4947
creatorWalletAddress: string;
50-
status: AccountStatus;
51-
plan: AccountPlan;
5248
name?: string;
5349
email?: string;
5450
advancedEnabled: boolean;
55-
currentBillingPeriodStartsAt: string;
56-
currentBillingPeriodEndsAt: string;
5751
emailConfirmedAt?: string;
5852
unconfirmedEmail?: string;
59-
trialPeriodEndedAt?: string;
6053
emailConfirmationWalletAddress?: string;
61-
stripePaymentActionUrl?: string;
6254
onboardSkipped?: boolean;
63-
paymentAttemptCount?: number;
6455
notificationPreferences?: {
6556
billing: "email" | "none";
6657
updates: "email" | "none";
6758
};
68-
recurringPaymentFailures: {
69-
subscriptionId: string;
70-
subscriptionDescription: string;
71-
paymentFailureCode: string;
72-
serviceCutoffDate: string;
73-
}[];
7459
// TODO - add image URL
7560
};
7661

@@ -194,33 +179,16 @@ export interface UpdateKeyInput {
194179
redirectUrls: string[];
195180
}
196181

197-
interface UsageBundler {
198-
chainId: number;
199-
sumTransactionFee: string;
200-
}
201-
202182
interface UsageStorage {
203183
sumFileSizeBytes: number;
204184
}
205185

206-
interface UsageEmbeddedWallets {
207-
countWalletAddresses: number;
208-
}
209-
210186
export interface UsageBillableByService {
211187
usage: {
212-
bundler: UsageBundler[];
213188
storage: UsageStorage;
214-
embeddedWallets: UsageEmbeddedWallets;
215-
};
216-
billableUsd: {
217-
bundler: number;
218-
storage: number;
219-
embeddedWallets: number;
220189
};
221190
limits: {
222191
storage: number;
223-
embeddedWallets: number;
224192
};
225193
rateLimits: {
226194
storage: number;
@@ -528,7 +496,7 @@ export function useConfirmEmail() {
528496
throw new Error(json.error.message);
529497
}
530498

531-
return json.data;
499+
return json.data as { team: Team; account: Account };
532500
},
533501
onSuccess: async () => {
534502
// invalidate related cache, since could be relinking account

apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/FaucetButton.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser";
1313
import { Turnstile } from "@marsidev/react-turnstile";
1414
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
1515
import type { CanClaimResponseType } from "app/api/testnet-faucet/can-claim/CanClaimResponseType";
16-
import { Onboarding } from "components/onboarding";
1716
import { mapV4ChainToV5Chain } from "contexts/map-chains";
1817
import { useTrack } from "hooks/analytics/useTrack";
19-
import { useState } from "react";
18+
import Link from "next/link";
19+
import { usePathname } from "next/navigation";
2020
import { useForm } from "react-hook-form";
2121
import { toast } from "sonner";
2222
import { toUnits } from "thirdweb";
2323
import type { ChainMetadata } from "thirdweb/chains";
2424
import { useActiveAccount, useWalletBalance } from "thirdweb/react";
2525
import { z } from "zod";
26+
import { isOnboardingComplete } from "../../../../../../login/isOnboardingRequired";
2627

2728
function formatTime(seconds: number) {
2829
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
@@ -52,6 +53,7 @@ export function FaucetButton({
5253
chain: ChainMetadata;
5354
amount: number;
5455
}) {
56+
const pathname = usePathname();
5557
const client = useThirdwebClient();
5658
const address = useActiveAccount()?.address;
5759
const chainId = chain.chainId;
@@ -118,7 +120,6 @@ export function FaucetButton({
118120

119121
const accountQuery = useAccount();
120122
const userQuery = useLoggedInUser();
121-
const [showOnboarding, setShowOnBoarding] = useState(false);
122123

123124
const canClaimFaucetQuery = useQuery({
124125
queryKey: ["testnet-faucet-can-claim", chainId],
@@ -145,7 +146,8 @@ export function FaucetButton({
145146
return (
146147
<CustomConnectWallet
147148
loginRequired={true}
148-
connectButtonClassName="!w-full !rounded !bg-primary !text-primary-foreground !px-4 !py-2 !text-sm"
149+
loadingButtonClassName="!w-full"
150+
signInLinkButtonClassName="!w-full !h-auto !rounded !bg-primary !text-primary-foreground !px-4 !py-2 !text-sm hover:!bg-primary/80"
149151
/>
150152
);
151153
}
@@ -201,23 +203,17 @@ export function FaucetButton({
201203
);
202204
}
203205

204-
// Email verification is required to claim from the faucet
205-
if (
206-
!accountQuery.data.emailConfirmedAt &&
207-
!accountQuery.data.unconfirmedEmail
208-
) {
206+
if (!isOnboardingComplete(accountQuery.data)) {
209207
return (
210-
<>
211-
<Button
212-
variant="outline"
213-
className="!opacity-100 w-full"
214-
onClick={() => setShowOnBoarding(true)}
208+
<Button asChild className="w-full">
209+
<Link
210+
href={
211+
pathname ? `/login?next=${encodeURIComponent(pathname)}` : "/login"
212+
}
215213
>
216214
Verify your Email
217-
</Button>
218-
{/* We will show the modal only if the user click on it, because this is a public page */}
219-
{showOnboarding && <Onboarding onOpenChange={setShowOnBoarding} />}
220-
</>
215+
</Link>
216+
</Button>
221217
);
222218
}
223219

0 commit comments

Comments
 (0)