mirror of
https://github.com/placeholder-soft/web.git
synced 2026-01-12 22:45:00 +08:00
ECO-258: Log error to DD (#769)
* Log error to DD in prod * log error * add context
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import ErrorsProvider from 'apps/web/contexts/Errors';
|
||||
import UsernameNav from 'apps/web/src/components/Layout/UsernameNav';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
@@ -25,9 +26,11 @@ export default async function BasenameLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="max-w-screen flex min-h-screen flex-col">
|
||||
<UsernameNav />
|
||||
{children}
|
||||
</div>
|
||||
<ErrorsProvider context="basenames">
|
||||
<div className="max-w-screen flex min-h-screen flex-col">
|
||||
<UsernameNav />
|
||||
{children}
|
||||
</div>
|
||||
</ErrorsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { redirect } from 'next/navigation';
|
||||
import classNames from 'classnames';
|
||||
import { BaseName } from '@coinbase/onchainkit/identity';
|
||||
import UsernameProfile from 'apps/web/src/components/Basenames/UsernameProfile';
|
||||
import ErrorsProvider from 'apps/web/contexts/Errors';
|
||||
|
||||
type UsernameProfileProps = {
|
||||
params: { username: BaseName };
|
||||
@@ -49,10 +50,12 @@ export default async function Username({ params }: UsernameProfileProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<ProfileProviders username={username} address={ensAddress}>
|
||||
<main className={usernameProfilePageClasses}>
|
||||
<UsernameProfile />
|
||||
</main>
|
||||
</ProfileProviders>
|
||||
<ErrorsProvider context="profile">
|
||||
<ProfileProviders username={username} address={ensAddress}>
|
||||
<main className={usernameProfilePageClasses}>
|
||||
<UsernameProfile />
|
||||
</main>
|
||||
</ProfileProviders>
|
||||
</ErrorsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import RegistrationProviders from 'apps/web/app/(basenames)/names/RegistrationProviders';
|
||||
import ErrorsProvider from 'apps/web/contexts/Errors';
|
||||
import RegistrationFlow from 'apps/web/src/components/Basenames/RegistrationFlow';
|
||||
import type { Metadata } from 'next';
|
||||
import { Suspense } from 'react';
|
||||
@@ -16,10 +17,12 @@ export const metadata: Metadata = {
|
||||
|
||||
export default async function Page() {
|
||||
return (
|
||||
<RegistrationProviders>
|
||||
<Suspense>
|
||||
<RegistrationFlow />
|
||||
</Suspense>
|
||||
</RegistrationProviders>
|
||||
<ErrorsProvider context="registration">
|
||||
<RegistrationProviders>
|
||||
<Suspense>
|
||||
<RegistrationFlow />
|
||||
</Suspense>
|
||||
</RegistrationProviders>
|
||||
</ErrorsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import { base, baseSepolia } from 'wagmi/chains';
|
||||
import { cookieManagerConfig } from '../src/utils/cookieManagerConfig';
|
||||
import ClientAnalyticsScript from 'apps/web/src/components/ClientAnalyticsScript/ClientAnalyticsScript';
|
||||
import dynamic from 'next/dynamic';
|
||||
import ErrorsProvider from 'apps/web/contexts/Errors';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
|
||||
const DynamicCookieBannerWrapper = dynamic(
|
||||
async () => import('apps/web/src/components/CookieBannerWrapper'),
|
||||
@@ -102,42 +104,42 @@ export default function AppProviders({ children }: AppProvidersProps) {
|
||||
|
||||
const handleLogError = useCallback((err: Error) => console.error(err), []);
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
useSprig(sprigEnvironmentId);
|
||||
|
||||
return (
|
||||
<CookieManagerProvider
|
||||
projectName="base_web"
|
||||
locale="en"
|
||||
region={Region.DEFAULT}
|
||||
log={console.log}
|
||||
onError={handleLogError}
|
||||
onPreferenceChange={setTrackingPreference}
|
||||
config={cookieManagerConfig}
|
||||
>
|
||||
<MotionConfig reducedMotion="user">
|
||||
<ClientAnalyticsScript />
|
||||
<WagmiProvider config={config}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<OnchainKitProvider
|
||||
chain={isDevelopment ? baseSepolia : base}
|
||||
apiKey={process.env.NEXT_PUBLIC_ONCHAINKIT_API_KEY}
|
||||
>
|
||||
<RainbowKitProvider modalSize="compact">
|
||||
<TooltipProvider>
|
||||
<ExperimentsProvider>
|
||||
<>
|
||||
{children}
|
||||
<DynamicCookieBannerWrapper />
|
||||
</>
|
||||
</ExperimentsProvider>
|
||||
</TooltipProvider>
|
||||
</RainbowKitProvider>
|
||||
</OnchainKitProvider>
|
||||
</QueryClientProvider>
|
||||
</WagmiProvider>
|
||||
</MotionConfig>
|
||||
</CookieManagerProvider>
|
||||
<ErrorsProvider context="web">
|
||||
<CookieManagerProvider
|
||||
projectName="base_web"
|
||||
locale="en"
|
||||
region={Region.DEFAULT}
|
||||
log={console.log}
|
||||
onError={handleLogError}
|
||||
onPreferenceChange={setTrackingPreference}
|
||||
config={cookieManagerConfig}
|
||||
>
|
||||
<MotionConfig reducedMotion="user">
|
||||
<ClientAnalyticsScript />
|
||||
<WagmiProvider config={config}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<OnchainKitProvider
|
||||
chain={isDevelopment ? baseSepolia : base}
|
||||
apiKey={process.env.NEXT_PUBLIC_ONCHAINKIT_API_KEY}
|
||||
>
|
||||
<RainbowKitProvider modalSize="compact">
|
||||
<TooltipProvider>
|
||||
<ExperimentsProvider>
|
||||
<>
|
||||
{children}
|
||||
<DynamicCookieBannerWrapper />
|
||||
</>
|
||||
</ExperimentsProvider>
|
||||
</TooltipProvider>
|
||||
</RainbowKitProvider>
|
||||
</OnchainKitProvider>
|
||||
</QueryClientProvider>
|
||||
</WagmiProvider>
|
||||
</MotionConfig>
|
||||
</CookieManagerProvider>
|
||||
</ErrorsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
'use client';
|
||||
|
||||
import { datadogRum } from '@datadog/browser-rum';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const nextPublicDatadogAppId = process.env.NEXT_PUBLIC_DATADOG_APP_ID ?? '';
|
||||
const nextPublicDatadogClientToken = process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN ?? '';
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
export default function DatadogInit() {
|
||||
useEffect(() => {
|
||||
|
||||
57
apps/web/contexts/Errors.tsx
Normal file
57
apps/web/contexts/Errors.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { datadogRum } from '@datadog/browser-rum';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
import { ReactNode, createContext, useCallback, useContext, useMemo } from 'react';
|
||||
|
||||
export type ErrorsContextProps = {
|
||||
logError: (error: unknown, message: string) => void;
|
||||
fullContext: string;
|
||||
};
|
||||
|
||||
export const ErrorsContext = createContext<ErrorsContextProps>({
|
||||
logError: function () {
|
||||
return undefined;
|
||||
},
|
||||
fullContext: '',
|
||||
});
|
||||
|
||||
export function useErrors() {
|
||||
const context = useContext(ErrorsContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useErrors must be used within a ErrorsProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
type ErrorsProviderProps = {
|
||||
children?: ReactNode;
|
||||
context: string;
|
||||
};
|
||||
|
||||
export default function ErrorsProvider({ children, context }: ErrorsProviderProps) {
|
||||
const { fullContext: previousContext } = useErrors();
|
||||
|
||||
const fullContext = [previousContext, context].filter((c) => !!c).join('_');
|
||||
|
||||
const logError = useCallback(
|
||||
(error: unknown, message: string) => {
|
||||
if (isDevelopment) {
|
||||
console.log('\n--------------------------------------');
|
||||
console.info(`Error caught with message: "${message}"`);
|
||||
console.error(error);
|
||||
console.info(`Context: "${fullContext}"`);
|
||||
console.log('--------------------------------------\n');
|
||||
return;
|
||||
}
|
||||
datadogRum.addError(error, { context: fullContext, message: message });
|
||||
},
|
||||
[fullContext],
|
||||
);
|
||||
|
||||
const values = useMemo(() => {
|
||||
return { logError, context, fullContext };
|
||||
}, [context, fullContext, logError]);
|
||||
|
||||
return <ErrorsContext.Provider value={values}>{children}</ErrorsContext.Provider>;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { namehash } from 'viem';
|
||||
import { getBasenamePublicClient } from 'apps/web/src/hooks/useBasenameChain';
|
||||
import L2ResolverAbi from 'apps/web/src/abis/L2Resolver';
|
||||
import { USERNAME_L2_RESOLVER_ADDRESSES } from 'apps/web/src/addresses/usernames';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
|
||||
const emojiCache: Record<string, Promise<string>> = {};
|
||||
|
||||
@@ -33,7 +34,7 @@ export default async function handler(request: NextRequest) {
|
||||
).then(async (res) => res.arrayBuffer());
|
||||
|
||||
const url = new URL(request.url);
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
const username = url.searchParams.get('name') ?? 'yourname';
|
||||
const domainName = isDevelopment ? `${url.protocol}//${url.host}` : 'https://www.base.org';
|
||||
const profilePicture = getUserNamePicture(username);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { USERNAME_L2_RESOLVER_ADDRESSES } from 'apps/web/src/addresses/usernames
|
||||
import L2ResolverAbi from 'apps/web/src/abis/L2Resolver';
|
||||
import { base } from 'viem/chains';
|
||||
import { getBasenamePublicClient } from 'apps/web/src/hooks/useBasenameChain';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
|
||||
export const config = {
|
||||
runtime: 'edge',
|
||||
@@ -19,7 +20,6 @@ export default async function handler(request: NextRequest) {
|
||||
).then(async (res) => res.arrayBuffer());
|
||||
|
||||
const url = new URL(request.url);
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
const username = url.searchParams.get('name') ?? 'yourname';
|
||||
const domainName = isDevelopment ? `${url.protocol}//${url.host}` : 'https://www.base.org';
|
||||
const profilePicture = getUserNamePicture(username);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { base } from 'viem/chains';
|
||||
|
||||
@@ -7,7 +8,6 @@ export const config = {
|
||||
|
||||
export default async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
const domainName = isDevelopment ? `${url.protocol}//${url.host}` : 'https://www.base.org';
|
||||
|
||||
const chainId = url.searchParams.get('chainId') ?? base.id;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { base } from 'viem/chains';
|
||||
|
||||
@@ -7,7 +8,7 @@ export const config = {
|
||||
|
||||
export default async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
const domainName = isDevelopment ? `${url.protocol}//${url.host}` : 'https://www.base.org';
|
||||
const chainId = url.searchParams.get('chainId') ?? base.id;
|
||||
if (!chainId) return NextResponse.json({ error: '406: chainId is missing' }, { status: 406 });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { premintMapping } from 'apps/web/pages/api/basenames/metadata/premintsMapping';
|
||||
import L2Resolver from 'apps/web/src/abis/L2Resolver';
|
||||
import { USERNAME_L2_RESOLVER_ADDRESSES } from 'apps/web/src/addresses/usernames';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
import { getBasenamePublicClient } from 'apps/web/src/hooks/useBasenameChain';
|
||||
import { USERNAME_DOMAINS } from 'apps/web/src/utils/usernames';
|
||||
import { NextResponse } from 'next/server';
|
||||
@@ -13,7 +14,7 @@ export const config = {
|
||||
|
||||
export default async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
const domainName = isDevelopment ? `${url.protocol}//${url.host}` : 'https://www.base.org';
|
||||
let tokenId = url.searchParams.get('tokenId');
|
||||
if (tokenId?.endsWith('.json')) tokenId = tokenId.slice(0, -5);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
import { useAnalytics } from 'apps/web/contexts/Analytics';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import {
|
||||
DiscountData,
|
||||
findFirstValidDiscount,
|
||||
@@ -113,6 +114,7 @@ export default function RegistrationProvider({ children }: RegistrationProviderP
|
||||
|
||||
// Analytics
|
||||
const { logEventWithContext } = useAnalytics();
|
||||
const { logError } = useErrors();
|
||||
|
||||
// Web3 data
|
||||
const { address } = useAccount();
|
||||
@@ -152,6 +154,7 @@ export default function RegistrationProvider({ children }: RegistrationProviderP
|
||||
enabled: !!registerNameTransactionHash,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerNameCallsBatchId, setRegisterNameCallsBatchId] = useState<string>('');
|
||||
|
||||
// The "correct" way to transition the UI would be to watch for call success, but this experimental
|
||||
@@ -174,7 +177,9 @@ export default function RegistrationProvider({ children }: RegistrationProviderP
|
||||
setRegistrationStep(RegistrationSteps.Success);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to refetch basename');
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
const redirectToProfile = useCallback(() => {
|
||||
@@ -232,6 +237,13 @@ export default function RegistrationProvider({ children }: RegistrationProviderP
|
||||
logEventWithContext('selected_name', ActionType.change);
|
||||
}, [logEventWithContext, selectedName]);
|
||||
|
||||
// Log error
|
||||
useEffect(() => {
|
||||
if (transactionError) {
|
||||
logError(transactionError, 'Failed to fetch the transaction receipt');
|
||||
}
|
||||
}, [logError, transactionError]);
|
||||
|
||||
const values = useMemo(() => {
|
||||
return {
|
||||
searchInputFocused,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '@heroicons/react/16/solid';
|
||||
import { ConnectButton, useConnectModal } from '@rainbow-me/rainbowkit';
|
||||
import { useAnalytics } from 'apps/web/contexts/Analytics';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import { useRegistration } from 'apps/web/src/components/Basenames/RegistrationContext';
|
||||
import RegistrationLearnMoreModal from 'apps/web/src/components/Basenames/RegistrationLearnMoreModal';
|
||||
import { Button, ButtonSizes, ButtonVariants } from 'apps/web/src/components/Button/Button';
|
||||
@@ -50,6 +51,7 @@ export default function RegistrationForm() {
|
||||
const chains = useChains();
|
||||
const { openConnectModal } = useConnectModal();
|
||||
const { logEventWithContext } = useAnalytics();
|
||||
const { logError } = useErrors();
|
||||
const { basenameChain } = useBasenameChain();
|
||||
const { switchChain } = useSwitchChain();
|
||||
const switchToIntendedNetwork = useCallback(
|
||||
@@ -132,8 +134,10 @@ export default function RegistrationForm() {
|
||||
const registerNameCallback = useCallback(() => {
|
||||
registerName()
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}, [registerName]);
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to register name');
|
||||
});
|
||||
}, [logError, registerName]);
|
||||
|
||||
const { data: balance } = useBalance({ address, chainId: connectedChain?.id });
|
||||
const insufficientBalanceToRegister =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useAnalytics } from 'apps/web/contexts/Analytics';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import {
|
||||
registrationTransitionDuration,
|
||||
useRegistration,
|
||||
@@ -35,6 +36,7 @@ export enum FormSteps {
|
||||
export default function RegistrationProfileForm() {
|
||||
const [currentFormStep, setCurrentFormStep] = useState<FormSteps>(FormSteps.Description);
|
||||
const [transitionStep, setTransitionStep] = useState<boolean>(false);
|
||||
const { logError } = useErrors();
|
||||
const { selectedName, redirectToProfile } = useRegistration();
|
||||
const { address } = useAccount();
|
||||
const { logEventWithContext } = useAnalytics();
|
||||
@@ -90,7 +92,9 @@ export default function RegistrationProfileForm() {
|
||||
.then(() => {
|
||||
redirectToProfile();
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to refetch text records');
|
||||
});
|
||||
}
|
||||
|
||||
if (transactionData.status === 'reverted') {
|
||||
@@ -106,6 +110,7 @@ export default function RegistrationProfileForm() {
|
||||
selectedName,
|
||||
basenameChain.id,
|
||||
redirectToProfile,
|
||||
logError,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -165,13 +170,16 @@ export default function RegistrationProfileForm() {
|
||||
redirectToProfile();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to write text records');
|
||||
});
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
},
|
||||
[
|
||||
currentFormStep,
|
||||
logError,
|
||||
logEventWithContext,
|
||||
redirectToProfile,
|
||||
textRecords,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { upload } from '@vercel/blob/client';
|
||||
import { useAnalytics } from 'apps/web/contexts/Analytics';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import UsernameAvatarField from 'apps/web/src/components/Basenames/UsernameAvatarField';
|
||||
import UsernameDescriptionField from 'apps/web/src/components/Basenames/UsernameDescriptionField';
|
||||
import UsernameKeywordsField from 'apps/web/src/components/Basenames/UsernameKeywordsField';
|
||||
@@ -35,6 +36,7 @@ export default function UsernameProfileEditModal({
|
||||
const { profileUsername, profileAddress, currentWalletIsOwner } = useUsernameProfile();
|
||||
const [avatarFile, setAvatarFile] = useState<File | undefined>();
|
||||
const { logEventWithContext } = useAnalytics();
|
||||
const { logError } = useErrors();
|
||||
const { basenameChain } = useBasenameChain(profileUsername);
|
||||
|
||||
const {
|
||||
@@ -88,7 +90,9 @@ export default function UsernameProfileEditModal({
|
||||
.then(() => {
|
||||
toggleModal();
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to refetch existing text records');
|
||||
});
|
||||
}
|
||||
|
||||
if (transactionData.status === 'reverted') {
|
||||
@@ -103,6 +107,7 @@ export default function UsernameProfileEditModal({
|
||||
logEventWithContext,
|
||||
transactionIsFetching,
|
||||
toggleModal,
|
||||
logError,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -174,25 +179,26 @@ export default function UsernameProfileEditModal({
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
logError(error, 'Update text records transaction canceled');
|
||||
logEventWithContext('update_text_records_transaction_canceled', ActionType.click);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to upload avatar');
|
||||
logEventWithContext('avatar_upload_failed', ActionType.error);
|
||||
});
|
||||
|
||||
logEventWithContext('update_text_records_transaction_initiated', ActionType.change);
|
||||
},
|
||||
[
|
||||
avatarFile,
|
||||
currentWalletIsOwner,
|
||||
logEventWithContext,
|
||||
uploadAvatar,
|
||||
textRecords,
|
||||
toggleModal,
|
||||
uploadAvatar,
|
||||
avatarFile,
|
||||
logEventWithContext,
|
||||
writeTextRecords,
|
||||
toggleModal,
|
||||
logError,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Divider } from 'apps/web/src/components/Divider/Divider';
|
||||
import { Job } from 'apps/web/src/components/Jobs/Job';
|
||||
import { greenhouseApiUrl } from 'apps/web/src/constants';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
|
||||
async function getJobs() {
|
||||
const res = await fetch(`${greenhouseApiUrl}/boards/basejobs/jobs?content=true`);
|
||||
@@ -30,11 +31,14 @@ export type JobType = {
|
||||
|
||||
export default function JobsList() {
|
||||
const [jobs, setJobs] = useState<JobType[]>([]);
|
||||
const { logError } = useErrors();
|
||||
useEffect(() => {
|
||||
getJobs()
|
||||
.then((js) => setJobs(js))
|
||||
.catch(console.error);
|
||||
}, []);
|
||||
.catch((error) => {
|
||||
logError(error, 'Failed to get jobs');
|
||||
});
|
||||
}, [logError]);
|
||||
|
||||
const departments = useMemo(() => {
|
||||
const departmentsById = jobs.reduce<DepartmentByIdReduceType>((acc, job) => {
|
||||
|
||||
@@ -13,10 +13,10 @@ import useBasenameChain from 'apps/web/src/hooks/useBasenameChain';
|
||||
import { base, baseSepolia } from 'viem/chains';
|
||||
import { Icon } from 'apps/web/src/components/Icon/Icon';
|
||||
import { useCallback } from 'react';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
|
||||
export default function UsernameNav() {
|
||||
const { isConnected } = useAccount();
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
const { basenameChain } = useBasenameChain();
|
||||
const { switchChain } = useSwitchChain();
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import { NameSuggestionResponseData } from 'apps/web/pages/api/name/[alreadyClaimedName]';
|
||||
import { useAreNamesAvailable } from 'apps/web/src/hooks/useIsNameAvailable';
|
||||
import { normalizeEnsDomainName, validateEnsDomainName } from 'apps/web/src/utils/usernames';
|
||||
@@ -7,7 +8,7 @@ export function useAlternativeNameSuggestions(nameNeedingAlternatives: string, d
|
||||
const [suggestions, setSuggestions] = useState<string[]>();
|
||||
const [error, setError] = useState<string>();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const { logError } = useErrors();
|
||||
useEffect(() => {
|
||||
async function checkAlternatives() {
|
||||
if (!doLookup || !nameNeedingAlternatives || nameNeedingAlternatives.length < 3) {
|
||||
@@ -21,14 +22,14 @@ export function useAlternativeNameSuggestions(nameNeedingAlternatives: string, d
|
||||
setSuggestions(suggestionData.suggestion);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('error checking for alternative names: ', e);
|
||||
logError(e, 'Failed to fetch alternative names');
|
||||
setError('error fetching name suggestions');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
void checkAlternatives();
|
||||
}, [doLookup, nameNeedingAlternatives]);
|
||||
}, [doLookup, logError, nameNeedingAlternatives]);
|
||||
|
||||
const normalizedNames = useMemo(
|
||||
() =>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import { Address, ReadContractErrorType, encodeAbiParameters } from 'viem';
|
||||
import { useAccount, useReadContract } from 'wagmi';
|
||||
import useBasenameChain from 'apps/web/src/hooks/useBasenameChain';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
|
||||
export type AttestationData = {
|
||||
discountValidatorAddress: Address;
|
||||
@@ -21,6 +22,7 @@ type AttestationHookReturns = {
|
||||
error: ReadContractErrorType | null;
|
||||
};
|
||||
export function useCheckCBIDAttestations(): AttestationHookReturns {
|
||||
const { logError } = useErrors();
|
||||
const { address } = useAccount();
|
||||
const [cBIDProofResponse, setCBIDProofResponse] = useState<CBIDProofResponse | null>(null);
|
||||
const { basenameChain } = useBasenameChain();
|
||||
@@ -35,15 +37,17 @@ export function useCheckCBIDAttestations(): AttestationHookReturns {
|
||||
const result = (await response.json()) as CBIDProofResponse;
|
||||
setCBIDProofResponse(result);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking CB.ID attestation:', e);
|
||||
} catch (error) {
|
||||
logError(error, 'Error checking CB.ID attestation');
|
||||
}
|
||||
}
|
||||
|
||||
if (address && !IS_EARLY_ACCESS) {
|
||||
checkCBIDAttestations(address).catch(console.error);
|
||||
checkCBIDAttestations(address).catch((error) => {
|
||||
logError(error, 'Error checking CB.ID attestation');
|
||||
});
|
||||
}
|
||||
}, [address, basenameChain.id]);
|
||||
}, [address, basenameChain.id, logError]);
|
||||
|
||||
const encodedProof = useMemo(
|
||||
() =>
|
||||
@@ -87,6 +91,7 @@ export function useCheckCBIDAttestations(): AttestationHookReturns {
|
||||
|
||||
// returns info about Coinbase verified account attestations
|
||||
export function useCheckCoinbaseAttestations() {
|
||||
const { logError } = useErrors();
|
||||
const { address } = useAccount();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [coinbaseProofResponse, setCoinbaseProofResponse] = useState<CoinbaseProofResponse | null>(
|
||||
@@ -106,17 +111,19 @@ export function useCheckCoinbaseAttestations() {
|
||||
if (response.ok) {
|
||||
setCoinbaseProofResponse(result);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking Coinbase account attestations:', e);
|
||||
} catch (error) {
|
||||
logError(error, 'Error checking Coinbase account attestations');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (address && !IS_EARLY_ACCESS) {
|
||||
checkCoinbaseAttestations(address).catch(console.error);
|
||||
checkCoinbaseAttestations(address).catch((error) => {
|
||||
logError(error, 'Error checking Coinbase account attestations');
|
||||
});
|
||||
}
|
||||
}, [address, basenameChain.id]);
|
||||
}, [address, basenameChain.id, logError]);
|
||||
|
||||
const signature = coinbaseProofResponse?.signedMessage as undefined | `0x${string}`;
|
||||
|
||||
@@ -149,6 +156,7 @@ export function useCheckCoinbaseAttestations() {
|
||||
}
|
||||
|
||||
export function useCheckCB1Attestations() {
|
||||
const { logError } = useErrors();
|
||||
const { address } = useAccount();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [cb1ProofResponse, setCB1ProofResponse] = useState<CoinbaseProofResponse | null>(null);
|
||||
@@ -165,17 +173,19 @@ export function useCheckCB1Attestations() {
|
||||
const result = (await response.json()) as CoinbaseProofResponse;
|
||||
setCB1ProofResponse(result);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking CB1 attestation:', e);
|
||||
} catch (error) {
|
||||
logError(error, 'Error checking CB1 attestation');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (address && !IS_EARLY_ACCESS) {
|
||||
checkCB1Attestations(address).catch(console.error);
|
||||
checkCB1Attestations(address).catch((error) => {
|
||||
logError(error, 'Error checking CB1 attestation');
|
||||
});
|
||||
}
|
||||
}, [address, basenameChain.id]);
|
||||
}, [address, basenameChain.id, logError]);
|
||||
|
||||
const signature = cb1ProofResponse?.signedMessage as undefined | `0x${string}`;
|
||||
|
||||
@@ -208,6 +218,7 @@ export function useCheckCB1Attestations() {
|
||||
}
|
||||
|
||||
export function useCheckEAAttestations(): AttestationHookReturns {
|
||||
const { logError } = useErrors();
|
||||
const { address } = useAccount();
|
||||
const [EAProofResponse, setEAProofResponse] = useState<EarlyAccessProofResponse | null>(null);
|
||||
const { basenameChain } = useBasenameChain();
|
||||
@@ -223,15 +234,17 @@ export function useCheckEAAttestations(): AttestationHookReturns {
|
||||
const result = (await response.json()) as EarlyAccessProofResponse;
|
||||
setEAProofResponse(result);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error checking early access:', e);
|
||||
} catch (error) {
|
||||
logError(error, 'Error checking early access');
|
||||
}
|
||||
}
|
||||
|
||||
if (address) {
|
||||
checkEarlyAccess(address).catch(console.error);
|
||||
checkEarlyAccess(address).catch((error) => {
|
||||
logError(error, 'Error checking early access');
|
||||
});
|
||||
}
|
||||
}, [address, basenameChain.id]);
|
||||
}, [address, basenameChain.id, logError]);
|
||||
|
||||
const encodedProof = useMemo(
|
||||
() =>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createPublicClient, http } from 'viem';
|
||||
import { cdpBaseRpcEndpoint, cdpBaseSepoliaRpcEndpoint } from 'apps/web/src/cdp/constants';
|
||||
import { BaseName } from '@coinbase/onchainkit/identity';
|
||||
import { getChainForBasename } from 'apps/web/src/utils/usernames';
|
||||
import { isDevelopment } from 'apps/web/src/constants';
|
||||
|
||||
export function getBasenamePublicClient(chainId: number) {
|
||||
const rpcEndpoint = chainId === baseSepolia.id ? cdpBaseSepoliaRpcEndpoint : cdpBaseRpcEndpoint;
|
||||
@@ -22,7 +23,6 @@ export function isBasenameSupportedChain(chainId: number) {
|
||||
}
|
||||
|
||||
export default function useBasenameChain(username?: BaseName) {
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
const { chain: connectedChain } = useAccount();
|
||||
const chains = useChains();
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function useBasenameChain(username?: BaseName) {
|
||||
|
||||
// Not connected, default to Sepolia for development, base for other envs
|
||||
return isDevelopment ? baseSepolia : base;
|
||||
}, [chains, connectedChain, isDevelopment, username]);
|
||||
}, [chains, connectedChain, username]);
|
||||
|
||||
const basenamePublicClient = getBasenamePublicClient(basenameChain.id);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useAnalytics } from 'apps/web/contexts/Analytics';
|
||||
import { useErrors } from 'apps/web/contexts/Errors';
|
||||
import L2ResolverAbi from 'apps/web/src/abis/L2Resolver';
|
||||
import { USERNAME_L2_RESOLVER_ADDRESSES } from 'apps/web/src/addresses/usernames';
|
||||
import useBasenameChain from 'apps/web/src/hooks/useBasenameChain';
|
||||
@@ -37,6 +38,7 @@ export function useRegisterNameCallback(
|
||||
): UseRegisterNameCallbackReturnValue {
|
||||
const { address, chainId, isConnected } = useAccount();
|
||||
const { basenameChain } = useBasenameChain();
|
||||
const { logError } = useErrors();
|
||||
const {
|
||||
data: callBatchId,
|
||||
writeContractsAsync,
|
||||
@@ -129,7 +131,7 @@ export function useRegisterNameCallback(
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('failed to register name', e);
|
||||
logError(e, 'Register name transaction canceled');
|
||||
logEventWithContext('register_name_transaction_canceled', ActionType.change);
|
||||
}
|
||||
}, [
|
||||
@@ -138,6 +140,7 @@ export function useRegisterNameCallback(
|
||||
capabilities,
|
||||
discountKey,
|
||||
isDiscounted,
|
||||
logError,
|
||||
logEventWithContext,
|
||||
name,
|
||||
normalizedName,
|
||||
|
||||
Reference in New Issue
Block a user