@erikey/react 0.4.23 → 0.4.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ui/index.mjs CHANGED
@@ -193,6 +193,18 @@ var UserRound = createLucideIcon("UserRound", [
193
193
  ["path", { d: "M20 21a8 8 0 0 0-16 0", key: "rfgkzh" }]
194
194
  ]);
195
195
 
196
+ // ../../../node_modules/.pnpm/lucide-react@0.462.0_react@19.0.0/node_modules/lucide-react/dist/esm/icons/wallet.js
197
+ var Wallet = createLucideIcon("Wallet", [
198
+ [
199
+ "path",
200
+ {
201
+ d: "M19 7V4a1 1 0 0 0-1-1H5a2 2 0 0 0 0 4h15a1 1 0 0 1 1 1v4h-3a2 2 0 0 0 0 4h3a1 1 0 0 0 1-1v-2a1 1 0 0 0-1-1",
202
+ key: "18etb6"
203
+ }
204
+ ],
205
+ ["path", { d: "M3 5v14a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1v-4", key: "xoc0q4" }]
206
+ ]);
207
+
196
208
  // src/ui/components/auth/auth-callback.tsx
197
209
  import { useContext as useContext4, useEffect as useEffect5, useRef as useRef2 } from "react";
198
210
 
@@ -13726,7 +13738,7 @@ function AuthForm({
13726
13738
  }
13727
13739
 
13728
13740
  // src/ui/components/auth/auth-view.tsx
13729
- import { useContext as useContext28, useEffect as useEffect22, useState as useState15 } from "react";
13741
+ import { useContext as useContext28, useEffect as useEffect22, useState as useState14 } from "react";
13730
13742
 
13731
13743
  // src/ui/components/provider-icons.tsx
13732
13744
  import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
@@ -14601,7 +14613,7 @@ function PasskeyButton({
14601
14613
  }
14602
14614
 
14603
14615
  // src/ui/components/auth/provider-button.tsx
14604
- import { useCallback as useCallback7, useContext as useContext27, useState as useState14 } from "react";
14616
+ import { useCallback as useCallback7, useContext as useContext27 } from "react";
14605
14617
  import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
14606
14618
  function ProviderButton({
14607
14619
  className,
@@ -14609,6 +14621,7 @@ function ProviderButton({
14609
14621
  callbackURL: callbackURLProp,
14610
14622
  isSubmitting,
14611
14623
  localization,
14624
+ onError,
14612
14625
  other,
14613
14626
  provider,
14614
14627
  redirectTo: redirectToProp,
@@ -14627,7 +14640,6 @@ function ProviderButton({
14627
14640
  toast,
14628
14641
  localizeErrors
14629
14642
  } = useContext27(AuthUIContext);
14630
- const [error, setError] = useState14(null);
14631
14643
  const getRedirectTo = useCallback7(
14632
14644
  () => redirectToProp || getSearchParam("redirectTo") || contextRedirectTo,
14633
14645
  [redirectToProp, contextRedirectTo]
@@ -14675,13 +14687,13 @@ function ProviderButton({
14675
14687
  await authClient.signIn.social(socialParams);
14676
14688
  }
14677
14689
  }
14678
- } catch (error2) {
14690
+ } catch (error) {
14679
14691
  const errorMessage = getLocalizedError({
14680
- error: error2,
14692
+ error,
14681
14693
  localization,
14682
14694
  localizeErrors
14683
14695
  });
14684
- setError(errorMessage);
14696
+ onError?.(errorMessage);
14685
14697
  toast({
14686
14698
  variant: "error",
14687
14699
  message: errorMessage
@@ -14689,33 +14701,27 @@ function ProviderButton({
14689
14701
  setIsSubmitting(false);
14690
14702
  }
14691
14703
  };
14692
- const handleClick = () => {
14693
- setError(null);
14694
- doSignInSocial();
14695
- };
14696
- return /* @__PURE__ */ jsxs22("div", { className: cn(socialLayout === "vertical" ? "w-full" : "grow", "flex flex-col gap-2"), children: [
14697
- /* @__PURE__ */ jsxs22(
14698
- Button,
14699
- {
14700
- className: cn(
14701
- "w-full",
14702
- className,
14703
- classNames?.form?.button,
14704
- classNames?.form?.outlineButton,
14705
- classNames?.form?.providerButton
14706
- ),
14707
- disabled: isSubmitting,
14708
- variant: "outline",
14709
- onClick: handleClick,
14710
- children: [
14711
- provider.icon && /* @__PURE__ */ jsx35(provider.icon, { className: classNames?.form?.icon }),
14712
- socialLayout === "grid" && provider.name,
14713
- socialLayout === "vertical" && `${localization.SIGN_IN_WITH} ${provider.name}`
14714
- ]
14715
- }
14716
- ),
14717
- error && /* @__PURE__ */ jsx35("div", { className: "rounded-md bg-destructive/10 p-2 text-xs text-destructive", children: error })
14718
- ] });
14704
+ return /* @__PURE__ */ jsxs22(
14705
+ Button,
14706
+ {
14707
+ className: cn(
14708
+ socialLayout === "vertical" && "w-full",
14709
+ socialLayout === "horizontal" && "flex-1 min-w-0",
14710
+ className,
14711
+ classNames?.form?.button,
14712
+ classNames?.form?.outlineButton,
14713
+ classNames?.form?.providerButton
14714
+ ),
14715
+ disabled: isSubmitting,
14716
+ variant: "outline",
14717
+ onClick: doSignInSocial,
14718
+ children: [
14719
+ provider.icon && /* @__PURE__ */ jsx35(provider.icon, { className: classNames?.form?.icon }),
14720
+ socialLayout === "grid" && provider.name,
14721
+ socialLayout === "vertical" && `${localization.SIGN_IN_WITH} ${provider.name}`
14722
+ ]
14723
+ }
14724
+ );
14719
14725
  }
14720
14726
 
14721
14727
  // src/ui/components/auth/auth-view.tsx
@@ -14756,7 +14762,12 @@ function AuthView({
14756
14762
  }
14757
14763
  const path = pathProp ?? pathname?.split("/").pop();
14758
14764
  const view = viewProp || getViewByPath(viewPaths, path) || "SIGN_IN";
14759
- const [isSubmitting, setIsSubmitting] = useState15(false);
14765
+ const [isSubmitting, setIsSubmitting] = useState14(false);
14766
+ const [providerError, setProviderError] = useState14(null);
14767
+ const handleSetIsSubmitting = (value) => {
14768
+ if (value) setProviderError(null);
14769
+ setIsSubmitting(value);
14770
+ };
14760
14771
  useEffect22(() => {
14761
14772
  const handlePageHide = () => setIsSubmitting(false);
14762
14773
  window.addEventListener("pagehide", handlePageHide);
@@ -14812,7 +14823,7 @@ function AuthView({
14812
14823
  otpSeparators,
14813
14824
  view,
14814
14825
  redirectTo,
14815
- setIsSubmitting
14826
+ setIsSubmitting: handleSetIsSubmitting
14816
14827
  }
14817
14828
  ),
14818
14829
  magicLink && (credentials && [
@@ -14902,9 +14913,10 @@ function AuthView({
14902
14913
  callbackURL,
14903
14914
  isSubmitting,
14904
14915
  localization,
14916
+ onError: setProviderError,
14905
14917
  provider: socialProvider,
14906
14918
  redirectTo,
14907
- setIsSubmitting,
14919
+ setIsSubmitting: handleSetIsSubmitting,
14908
14920
  socialLayout
14909
14921
  },
14910
14922
  provider
@@ -14918,9 +14930,10 @@ function AuthView({
14918
14930
  callbackURL,
14919
14931
  isSubmitting,
14920
14932
  localization,
14933
+ onError: setProviderError,
14921
14934
  provider,
14922
14935
  redirectTo,
14923
- setIsSubmitting,
14936
+ setIsSubmitting: handleSetIsSubmitting,
14924
14937
  socialLayout,
14925
14938
  other: true
14926
14939
  },
@@ -14930,6 +14943,7 @@ function AuthView({
14930
14943
  ]
14931
14944
  }
14932
14945
  ),
14946
+ providerError && /* @__PURE__ */ jsx36("div", { className: "rounded-md bg-destructive/10 p-3 text-sm text-destructive", children: providerError }),
14933
14947
  passkey && [
14934
14948
  "SIGN_IN",
14935
14949
  "MAGIC_LINK",
@@ -14944,7 +14958,7 @@ function AuthView({
14944
14958
  isSubmitting,
14945
14959
  localization,
14946
14960
  redirectTo,
14947
- setIsSubmitting
14961
+ setIsSubmitting: handleSetIsSubmitting
14948
14962
  }
14949
14963
  )
14950
14964
  ] })
@@ -15000,18 +15014,219 @@ function AuthView({
15000
15014
  ] });
15001
15015
  }
15002
15016
 
15017
+ // src/ui/components/auth/wallet-button.tsx
15018
+ import { useContext as useContext29, useState as useState15, useEffect as useEffect23, useCallback as useCallback8 } from "react";
15019
+
15020
+ // src/ui/lib/wallet.ts
15021
+ function hasInjectedWallet() {
15022
+ return typeof window !== "undefined" && !!window.ethereum;
15023
+ }
15024
+ async function connectWallet() {
15025
+ if (!window.ethereum) {
15026
+ throw new Error("No wallet found. Please install MetaMask or Phantom.");
15027
+ }
15028
+ const accounts = await window.ethereum.request({
15029
+ method: "eth_requestAccounts"
15030
+ });
15031
+ if (!accounts || accounts.length === 0) {
15032
+ throw new Error("No accounts found");
15033
+ }
15034
+ const chainIdHex = await window.ethereum.request({
15035
+ method: "eth_chainId"
15036
+ });
15037
+ const address = toChecksumAddress(accounts[0]);
15038
+ return {
15039
+ address,
15040
+ chainId: parseInt(chainIdHex, 16)
15041
+ };
15042
+ }
15043
+ async function signMessage(message, address) {
15044
+ if (!window.ethereum) {
15045
+ throw new Error("No wallet found");
15046
+ }
15047
+ return window.ethereum.request({
15048
+ method: "personal_sign",
15049
+ params: [message, address]
15050
+ });
15051
+ }
15052
+ function buildSiweMessage(opts) {
15053
+ const issuedAt = opts.issuedAt || (/* @__PURE__ */ new Date()).toISOString();
15054
+ return `${opts.domain} wants you to sign in with your Ethereum account:
15055
+ ${opts.address}
15056
+
15057
+ ${opts.statement}
15058
+
15059
+ URI: ${opts.uri}
15060
+ Version: ${opts.version}
15061
+ Chain ID: ${opts.chainId}
15062
+ Nonce: ${opts.nonce}
15063
+ Issued At: ${issuedAt}`;
15064
+ }
15065
+ function toChecksumAddress(address) {
15066
+ const addr = address.toLowerCase().replace("0x", "");
15067
+ if (address.match(/^0x[0-9a-fA-F]{40}$/)) {
15068
+ if (address !== address.toLowerCase() && address !== address.toUpperCase()) {
15069
+ return address;
15070
+ }
15071
+ }
15072
+ return address;
15073
+ }
15074
+
15075
+ // src/ui/components/auth/wallet-button.tsx
15076
+ import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
15077
+ function WalletButton({
15078
+ classNames,
15079
+ isSubmitting,
15080
+ localization,
15081
+ redirectTo,
15082
+ setIsSubmitting
15083
+ }) {
15084
+ const {
15085
+ authClient,
15086
+ localization: contextLocalization,
15087
+ toast,
15088
+ localizeErrors
15089
+ } = useContext29(AuthUIContext);
15090
+ localization = { ...contextLocalization, ...localization };
15091
+ const { onSuccess } = useOnSuccessTransition({ redirectTo });
15092
+ const [mounted, setMounted] = useState15(false);
15093
+ const [walletAddress, setWalletAddress] = useState15(null);
15094
+ const [chainId, setChainId] = useState15(null);
15095
+ useEffect23(() => {
15096
+ setMounted(true);
15097
+ }, []);
15098
+ const handleWalletAuth = useCallback8(async () => {
15099
+ setIsSubmitting?.(true);
15100
+ try {
15101
+ let address = walletAddress;
15102
+ let chain = chainId;
15103
+ if (!address || !chain) {
15104
+ const connection = await connectWallet();
15105
+ address = connection.address;
15106
+ chain = connection.chainId;
15107
+ setWalletAddress(address);
15108
+ setChainId(chain);
15109
+ }
15110
+ const nonceResult = await authClient.siwe.nonce({
15111
+ walletAddress: address,
15112
+ chainId: chain
15113
+ });
15114
+ if (nonceResult.error) {
15115
+ throw new Error(nonceResult.error.message || "Failed to get nonce");
15116
+ }
15117
+ const nonce = nonceResult.data?.nonce;
15118
+ if (!nonce) {
15119
+ throw new Error("No nonce received from server");
15120
+ }
15121
+ const message = buildSiweMessage({
15122
+ domain: window.location.host,
15123
+ address,
15124
+ statement: "Sign in with Ethereum",
15125
+ uri: window.location.origin,
15126
+ version: "1",
15127
+ chainId: chain,
15128
+ nonce
15129
+ });
15130
+ const signature = await signMessage(message, address);
15131
+ const verifyResult = await authClient.siwe.verify({
15132
+ message,
15133
+ signature,
15134
+ walletAddress: address,
15135
+ chainId: chain
15136
+ });
15137
+ if (verifyResult.error) {
15138
+ throw new Error(verifyResult.error.message || "Verification failed");
15139
+ }
15140
+ onSuccess();
15141
+ } catch (error) {
15142
+ toast({
15143
+ variant: "error",
15144
+ message: getLocalizedError({
15145
+ error,
15146
+ localization,
15147
+ localizeErrors
15148
+ })
15149
+ });
15150
+ setIsSubmitting?.(false);
15151
+ }
15152
+ }, [
15153
+ authClient,
15154
+ walletAddress,
15155
+ chainId,
15156
+ localization,
15157
+ localizeErrors,
15158
+ onSuccess,
15159
+ setIsSubmitting,
15160
+ toast
15161
+ ]);
15162
+ if (!mounted) {
15163
+ return /* @__PURE__ */ jsxs24(
15164
+ Button,
15165
+ {
15166
+ className: cn(
15167
+ "w-full",
15168
+ classNames?.form?.button,
15169
+ classNames?.form?.secondaryButton
15170
+ ),
15171
+ disabled: true,
15172
+ variant: "secondary",
15173
+ children: [
15174
+ /* @__PURE__ */ jsx37(Wallet, { className: "h-4 w-4" }),
15175
+ "Connect Wallet"
15176
+ ]
15177
+ }
15178
+ );
15179
+ }
15180
+ if (!hasInjectedWallet()) {
15181
+ return /* @__PURE__ */ jsxs24(
15182
+ Button,
15183
+ {
15184
+ className: cn(
15185
+ "w-full",
15186
+ classNames?.form?.button,
15187
+ classNames?.form?.secondaryButton
15188
+ ),
15189
+ disabled: true,
15190
+ variant: "secondary",
15191
+ title: "Install MetaMask or Phantom to use wallet sign-in",
15192
+ children: [
15193
+ /* @__PURE__ */ jsx37(Wallet, { className: "h-4 w-4" }),
15194
+ "No Wallet Found"
15195
+ ]
15196
+ }
15197
+ );
15198
+ }
15199
+ return /* @__PURE__ */ jsxs24(
15200
+ Button,
15201
+ {
15202
+ className: cn(
15203
+ "w-full",
15204
+ classNames?.form?.button,
15205
+ classNames?.form?.secondaryButton
15206
+ ),
15207
+ disabled: isSubmitting,
15208
+ variant: "secondary",
15209
+ onClick: handleWalletAuth,
15210
+ children: [
15211
+ /* @__PURE__ */ jsx37(Wallet, { className: "h-4 w-4" }),
15212
+ walletAddress ? `Sign in with ${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}` : "Sign in with Wallet"
15213
+ ]
15214
+ }
15215
+ );
15216
+ }
15217
+
15003
15218
  // src/ui/components/auth-loading.tsx
15004
- import { useContext as useContext29 } from "react";
15219
+ import { useContext as useContext30 } from "react";
15005
15220
  function AuthLoading({ children }) {
15006
15221
  const {
15007
15222
  hooks: { useSession }
15008
- } = useContext29(AuthUIContext);
15223
+ } = useContext30(AuthUIContext);
15009
15224
  const { isPending } = useSession();
15010
15225
  return isPending ? children : null;
15011
15226
  }
15012
15227
 
15013
15228
  // src/ui/hooks/use-authenticate.ts
15014
- import { useContext as useContext30, useEffect as useEffect23 } from "react";
15229
+ import { useContext as useContext31, useEffect as useEffect24 } from "react";
15015
15230
  function useAuthenticate(options) {
15016
15231
  const { authView = "SIGN_IN", enabled = true } = options ?? {};
15017
15232
  const {
@@ -15019,10 +15234,10 @@ function useAuthenticate(options) {
15019
15234
  basePath,
15020
15235
  viewPaths,
15021
15236
  replace
15022
- } = useContext30(AuthUIContext);
15237
+ } = useContext31(AuthUIContext);
15023
15238
  const { data, isPending, error, refetch } = useSession();
15024
15239
  const sessionData = data;
15025
- useEffect23(() => {
15240
+ useEffect24(() => {
15026
15241
  if (!enabled || isPending || sessionData) return;
15027
15242
  const searchParams = new URLSearchParams(window.location.search);
15028
15243
  const redirectTo = searchParams.get("redirectTo") || window.location.pathname + window.location.search;
@@ -15060,21 +15275,21 @@ function RedirectToSignUp() {
15060
15275
  }
15061
15276
 
15062
15277
  // src/ui/components/signed-in.tsx
15063
- import { useContext as useContext31 } from "react";
15278
+ import { useContext as useContext32 } from "react";
15064
15279
  function SignedIn({ children }) {
15065
15280
  const {
15066
15281
  hooks: { useSession }
15067
- } = useContext31(AuthUIContext);
15282
+ } = useContext32(AuthUIContext);
15068
15283
  const { data } = useSession();
15069
15284
  return data ? children : null;
15070
15285
  }
15071
15286
 
15072
15287
  // src/ui/components/signed-out.tsx
15073
- import { useContext as useContext32 } from "react";
15288
+ import { useContext as useContext33 } from "react";
15074
15289
  function SignedOut({ children }) {
15075
15290
  const {
15076
15291
  hooks: { useSession }
15077
- } = useContext32(AuthUIContext);
15292
+ } = useContext33(AuthUIContext);
15078
15293
  const { data, isPending } = useSession();
15079
15294
  return !data && !isPending ? children : null;
15080
15295
  }
@@ -15131,6 +15346,7 @@ export {
15131
15346
  UserAvatar,
15132
15347
  VKIcon,
15133
15348
  VercelIcon,
15349
+ WalletButton,
15134
15350
  XIcon,
15135
15351
  ZoomIcon,
15136
15352
  accountViewPaths,
@@ -15162,6 +15378,7 @@ lucide-react/dist/esm/icons/qr-code.js:
15162
15378
  lucide-react/dist/esm/icons/send.js:
15163
15379
  lucide-react/dist/esm/icons/trash-2.js:
15164
15380
  lucide-react/dist/esm/icons/user-round.js:
15381
+ lucide-react/dist/esm/icons/wallet.js:
15165
15382
  lucide-react/dist/esm/lucide-react.js:
15166
15383
  (**
15167
15384
  * @license lucide-react v0.462.0 - ISC