@magic-ext/magic-widget 1.0.0-canary.979.21008432451.0 → 1.0.0-canary.979.21048249937.0

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/cjs/index.js CHANGED
@@ -13963,20 +13963,23 @@ const ProviderButton = ({ label, Icon, onPress, hideLabel }) => {
13963
13963
  !hideLabel && label && (React.createElement(M$8, { fontWeight: "medium", styles: { lineHeight: '1.5rem' } }, label)))));
13964
13964
  };
13965
13965
 
13966
- var ThirdPartyWallets;
13967
- (function (ThirdPartyWallets) {
13968
- ThirdPartyWallets["METAMASK"] = "metamask";
13969
- ThirdPartyWallets["WALLETCONNECT"] = "walletconnect";
13970
- ThirdPartyWallets["COINBASE"] = "coinbase";
13971
- ThirdPartyWallets["PHANTOM"] = "phantom";
13972
- ThirdPartyWallets["RABBY"] = "rabby";
13973
- })(ThirdPartyWallets || (ThirdPartyWallets = {}));
13966
+ /**
13967
+ * Available third-party wallet providers.
13968
+ * Use these constants or pass string literals directly: 'metamask', 'coinbase', etc.
13969
+ */
13970
+ const ThirdPartyWallets = {
13971
+ METAMASK: 'metamask',
13972
+ WALLETCONNECT: 'walletconnect',
13973
+ COINBASE: 'coinbase',
13974
+ PHANTOM: 'phantom',
13975
+ RABBY: 'rabby',
13976
+ };
13974
13977
  var RpcErrorMessage;
13975
13978
  (function (RpcErrorMessage) {
13976
13979
  RpcErrorMessage["MalformedEmail"] = "Invalid params: Please provide a valid email address.";
13977
13980
  RpcErrorMessage["SanEmail"] = "We are unable to create an account with that email.";
13978
13981
  })(RpcErrorMessage || (RpcErrorMessage = {}));
13979
- var OAuthProvider;
13982
+ exports.OAuthProvider = void 0;
13980
13983
  (function (OAuthProvider) {
13981
13984
  OAuthProvider["GOOGLE"] = "google";
13982
13985
  OAuthProvider["APPLE"] = "apple";
@@ -13990,7 +13993,7 @@ var OAuthProvider;
13990
13993
  OAuthProvider["DISCORD"] = "discord";
13991
13994
  OAuthProvider["GITLAB"] = "gitlab";
13992
13995
  OAuthProvider["TELEGRAM"] = "telegram";
13993
- })(OAuthProvider || (OAuthProvider = {}));
13996
+ })(exports.OAuthProvider || (exports.OAuthProvider = {}));
13994
13997
 
13995
13998
  const WALLET_METADATA = {
13996
13999
  [ThirdPartyWallets.WALLETCONNECT]: { displayName: 'WalletConnect', Icon: e },
@@ -14000,18 +14003,18 @@ const WALLET_METADATA = {
14000
14003
  [ThirdPartyWallets.RABBY]: { displayName: 'Rabby', Icon: r },
14001
14004
  };
14002
14005
  const OAUTH_METADATA = {
14003
- [OAuthProvider.GOOGLE]: { displayName: 'Google', Icon: L$2 },
14004
- [OAuthProvider.APPLE]: { displayName: 'Apple', Icon: d$4 },
14005
- [OAuthProvider.FACEBOOK]: { displayName: 'Facebook', Icon: a },
14006
- [OAuthProvider.GITHUB]: { displayName: 'GitHub', Icon: d$2 },
14007
- [OAuthProvider.TWITTER]: { displayName: 'Twitter', Icon: r$1 },
14008
- [OAuthProvider.LINKEDIN]: { displayName: 'LinkedIn', Icon: n$1 },
14009
- [OAuthProvider.MICROSOFT]: { displayName: 'Microsoft', Icon: d$1 },
14010
- [OAuthProvider.TWITCH]: { displayName: 'Twitch', Icon: H$1 },
14011
- [OAuthProvider.BITBUCKET]: { displayName: 'Bitbucket', Icon: n$2 },
14012
- [OAuthProvider.DISCORD]: { displayName: 'Discord', Icon: d$3 },
14013
- [OAuthProvider.GITLAB]: { displayName: 'GitLab', Icon: e$1 },
14014
- [OAuthProvider.TELEGRAM]: { displayName: 'Telegram', Icon: r$2 },
14006
+ [exports.OAuthProvider.GOOGLE]: { displayName: 'Google', Icon: L$2 },
14007
+ [exports.OAuthProvider.APPLE]: { displayName: 'Apple', Icon: d$4 },
14008
+ [exports.OAuthProvider.FACEBOOK]: { displayName: 'Facebook', Icon: a },
14009
+ [exports.OAuthProvider.GITHUB]: { displayName: 'GitHub', Icon: d$2 },
14010
+ [exports.OAuthProvider.TWITTER]: { displayName: 'Twitter', Icon: r$1 },
14011
+ [exports.OAuthProvider.LINKEDIN]: { displayName: 'LinkedIn', Icon: n$1 },
14012
+ [exports.OAuthProvider.MICROSOFT]: { displayName: 'Microsoft', Icon: d$1 },
14013
+ [exports.OAuthProvider.TWITCH]: { displayName: 'Twitch', Icon: H$1 },
14014
+ [exports.OAuthProvider.BITBUCKET]: { displayName: 'Bitbucket', Icon: n$2 },
14015
+ [exports.OAuthProvider.DISCORD]: { displayName: 'Discord', Icon: d$3 },
14016
+ [exports.OAuthProvider.GITLAB]: { displayName: 'GitLab', Icon: e$1 },
14017
+ [exports.OAuthProvider.TELEGRAM]: { displayName: 'Telegram', Icon: r$2 },
14015
14018
  };
14016
14019
 
14017
14020
  /**
@@ -14375,8 +14378,48 @@ class MagicWidgetExtension extends provider.Extension.Internal {
14375
14378
  }
14376
14379
  }
14377
14380
 
14381
+ const WidgetConfigContext = React.createContext(null);
14382
+ function WidgetConfigProvider({ children, wallets = [], onSuccess, onError, onClose, closeOnSuccess = false, }) {
14383
+ const handleSuccess = React.useCallback((result) => {
14384
+ onSuccess?.(result);
14385
+ if (closeOnSuccess && onClose) {
14386
+ // Delay closing so users can see the success screen
14387
+ setTimeout(() => {
14388
+ onClose();
14389
+ }, 2000);
14390
+ }
14391
+ }, [onSuccess, closeOnSuccess, onClose]);
14392
+ const handleError = React.useCallback((error) => {
14393
+ onError?.(error);
14394
+ }, [onError]);
14395
+ const handleClose = onClose
14396
+ ? () => {
14397
+ onClose();
14398
+ }
14399
+ : undefined;
14400
+ const value = {
14401
+ wallets,
14402
+ handleSuccess,
14403
+ handleError,
14404
+ handleClose,
14405
+ };
14406
+ return React.createElement(WidgetConfigContext.Provider, { value: value }, children);
14407
+ }
14408
+ /**
14409
+ * Hook to access the widget configuration
14410
+ * @throws Error if used outside of WidgetConfigProvider
14411
+ */
14412
+ function useWidgetConfig() {
14413
+ const context = React.useContext(WidgetConfigContext);
14414
+ if (!context) {
14415
+ throw new Error('useWidgetConfig must be used within a WidgetConfigProvider');
14416
+ }
14417
+ return context;
14418
+ }
14419
+
14378
14420
  const EmailLoginContext = React.createContext(null);
14379
14421
  function EmailLoginProvider({ children, dispatch }) {
14422
+ const { handleSuccess, handleError } = useWidgetConfig();
14380
14423
  // Store the current login handle
14381
14424
  const handleRef = React.useRef(null);
14382
14425
  const emailRef = React.useRef(null);
@@ -14455,19 +14498,24 @@ function EmailLoginProvider({ children, dispatch }) {
14455
14498
  .then(didToken => {
14456
14499
  if (didToken) {
14457
14500
  dispatch({ type: 'LOGIN_SUCCESS' });
14501
+ handleSuccess({ method: 'email', didToken });
14458
14502
  }
14459
14503
  })
14460
14504
  .catch(error => {
14461
- dispatch({ type: 'LOGIN_ERROR', error: error?.message || 'Login failed' });
14505
+ const errorInstance = error instanceof Error ? error : new Error(error?.message || 'Login failed');
14506
+ dispatch({ type: 'LOGIN_ERROR', error: errorInstance.message });
14507
+ handleError(errorInstance);
14462
14508
  });
14463
14509
  }
14464
14510
  catch (error) {
14511
+ const errorInstance = error instanceof Error ? error : new Error('Failed to start login');
14465
14512
  dispatch({
14466
14513
  type: 'LOGIN_ERROR',
14467
- error: error instanceof Error ? error.message : 'Failed to start login',
14514
+ error: errorInstance.message,
14468
14515
  });
14516
+ handleError(errorInstance);
14469
14517
  }
14470
- }, [dispatch]);
14518
+ }, [dispatch, handleSuccess, handleError]);
14471
14519
  /**
14472
14520
  * Submit OTP code for verification
14473
14521
  */
@@ -14616,10 +14664,7 @@ const SocialProviders = ({ providers, onPress, dispatch }) => {
14616
14664
  };
14617
14665
 
14618
14666
  function WidgetHeader({ showHeaderText = true, onPressBack }) {
14619
- const handleClose = () => {
14620
- // TODO: Need to determine what this behavior should be/if it should exist at all
14621
- console.log('Close widget');
14622
- };
14667
+ const { handleClose } = useWidgetConfig();
14623
14668
  return (React.createElement(u$3, { position: "relative" },
14624
14669
  !!onPressBack && (React.createElement(u$3.LeftAction, null,
14625
14670
  React.createElement(Q$2, { size: "sm", variant: "neutral", onPress: onPressBack },
@@ -14627,19 +14672,19 @@ function WidgetHeader({ showHeaderText = true, onPressBack }) {
14627
14672
  React.createElement(s$6, null))))),
14628
14673
  showHeaderText && (React.createElement(u$3.Content, null,
14629
14674
  React.createElement(M$8, { size: "sm", fontColor: "text.tertiary" }, "Log in or sign up"))),
14630
- React.createElement(u$3.RightAction, null,
14675
+ handleClose && (React.createElement(u$3.RightAction, null,
14631
14676
  React.createElement(Q$2, { size: "sm", variant: "neutral", onPress: handleClose },
14632
14677
  React.createElement(Q$2.TrailingIcon, null,
14633
- React.createElement(c$a, null))))));
14678
+ React.createElement(c$a, null)))))));
14634
14679
  }
14635
14680
 
14636
14681
  const LoginView = ({ dispatch }) => {
14637
14682
  const config = getExtensionInstance().getConfig();
14683
+ const { wallets } = useWidgetConfig();
14638
14684
  const { primary, social } = config?.authProviders ?? {};
14639
14685
  const hasEmailProvider = primary?.includes('email');
14640
14686
  const socialProviders = social?.map(provider => provider) ?? [];
14641
- const enabledWalletProviders = Object.values(ThirdPartyWallets).filter(provider => provider !== ThirdPartyWallets.WALLETCONNECT);
14642
- const showDivider = socialProviders.length > 0 && enabledWalletProviders.length > 0;
14687
+ const showDivider = (hasEmailProvider || socialProviders.length > 0) && wallets.length > 0;
14643
14688
  const handleProviderSelect = (provider) => {
14644
14689
  dispatch({ type: 'SELECT_WALLET', provider });
14645
14690
  };
@@ -14657,11 +14702,11 @@ const LoginView = ({ dispatch }) => {
14657
14702
  React.createElement(Divider, { color: "surface.quaternary" }),
14658
14703
  React.createElement(M$8, { "aria-label": "or", fontColor: "text.tertiary" }, "or"),
14659
14704
  React.createElement(Divider, { color: "surface.quaternary" }))),
14660
- enabledWalletProviders.length > 0 && (React.createElement(HStack, { gap: 2, w: "full" }, enabledWalletProviders.map(provider => (React.createElement(ProviderButton, { key: provider, hideLabel: enabledWalletProviders.length > 1, label: WALLET_METADATA[provider].displayName, Icon: WALLET_METADATA[provider].Icon, onPress: () => handleProviderSelect(provider) })))))))));
14705
+ wallets.length > 0 && (React.createElement(Flex, { gap: 2, w: "full", direction: showDivider ? 'row' : 'column', justify: "center" }, wallets.map(provider => (React.createElement(ProviderButton, { key: provider, hideLabel: wallets.length > 1 && showDivider, label: WALLET_METADATA[provider].displayName, Icon: WALLET_METADATA[provider].Icon, onPress: () => handleProviderSelect(provider) })))))))));
14661
14706
  };
14662
14707
 
14663
14708
  function getProviderConfig(provider) {
14664
- const isOAuth = Object.values(OAuthProvider).includes(provider);
14709
+ const isOAuth = Object.values(exports.OAuthProvider).includes(provider);
14665
14710
  const metadata = isOAuth ? OAUTH_METADATA[provider] : WALLET_METADATA[provider];
14666
14711
  const { displayName, Icon } = metadata;
14667
14712
  if (isOAuth) {
@@ -14679,7 +14724,7 @@ function getProviderConfig(provider) {
14679
14724
  }
14680
14725
 
14681
14726
  /**
14682
- * Map ThirdPartyWallets enum to wagmi connector IDs
14727
+ * Map wallet types to wagmi connector IDs
14683
14728
  */
14684
14729
  const CONNECTOR_IDS = {
14685
14730
  [ThirdPartyWallets.METAMASK]: 'metaMaskSDK',
@@ -14767,15 +14812,16 @@ function useWalletConnect(provider) {
14767
14812
  function useSiweLogin() {
14768
14813
  const { signMessageAsync } = wagmi.useSignMessage();
14769
14814
  const connectedChainId = wagmi.useChainId();
14815
+ const { handleSuccess, handleError } = useWidgetConfig();
14770
14816
  const [isLoading, setIsLoading] = React.useState(false);
14771
14817
  const [error, setError] = React.useState(null);
14772
14818
  const [isSuccess, setIsSuccess] = React.useState(false);
14773
- const [publicAddress, setPublicAddress] = React.useState(null);
14819
+ const [walletAddress, setWalletAddress] = React.useState(null);
14774
14820
  const performSiweLogin = React.useCallback(async (address, chainId) => {
14775
14821
  setIsLoading(true);
14776
14822
  setError(null);
14777
14823
  setIsSuccess(false);
14778
- setPublicAddress(null);
14824
+ setWalletAddress(null);
14779
14825
  try {
14780
14826
  const extension = getExtensionInstance();
14781
14827
  const effectiveChainId = chainId || connectedChainId || 1;
@@ -14787,28 +14833,30 @@ function useSiweLogin() {
14787
14833
  // Step 2: Sign the message with the connected wallet
14788
14834
  const signature = await signMessageAsync({ message });
14789
14835
  // Step 3: Send the signed message to Magic backend for verification
14790
- const publicAddress = await extension.login({ message, signature });
14836
+ await extension.login({ message, signature });
14791
14837
  // Step 4: Set up the connected state for 3rd party wallet RPC routing
14792
14838
  // This enables signing requests to be routed through the connected wallet
14793
14839
  extension.setConnectedState(address, effectiveChainId);
14794
14840
  setIsSuccess(true);
14795
- setPublicAddress(publicAddress);
14841
+ setWalletAddress(address);
14796
14842
  setIsLoading(false);
14797
- return publicAddress;
14843
+ handleSuccess({ method: 'wallet', walletAddress: address });
14844
+ return address;
14798
14845
  }
14799
14846
  catch (err) {
14800
14847
  const errorInstance = err instanceof Error ? err : new Error('SIWE login failed');
14801
14848
  setError(errorInstance);
14802
14849
  setIsLoading(false);
14850
+ handleError(errorInstance);
14803
14851
  throw errorInstance;
14804
14852
  }
14805
- }, [signMessageAsync, connectedChainId]);
14853
+ }, [signMessageAsync, connectedChainId, handleSuccess, handleError]);
14806
14854
  return {
14807
14855
  performSiweLogin,
14808
14856
  isLoading,
14809
14857
  error,
14810
14858
  isSuccess,
14811
- publicAddress,
14859
+ walletAddress,
14812
14860
  };
14813
14861
  }
14814
14862
 
@@ -14820,7 +14868,7 @@ const centeredIconClass = css({
14820
14868
  });
14821
14869
  const Pending = ({ onPressBack, title, description, Icon, isPending, errorMessage }) => {
14822
14870
  return (React.createElement(React.Fragment, null,
14823
- React.createElement(WidgetHeader, { onPressBack: onPressBack, showHeaderText: false }),
14871
+ React.createElement(WidgetHeader, { onPressBack: isPending ? onPressBack : undefined, showHeaderText: false }),
14824
14872
  React.createElement(VStack, { gap: 6, pt: 4 },
14825
14873
  React.createElement(Box, { position: "relative", h: 20, w: 20 },
14826
14874
  isPending && React.createElement(d$m, { size: 80, strokeWidth: 8, neutral: true, progress: 40 }),
@@ -15063,6 +15111,7 @@ function widgetReducer(state, action) {
15063
15111
  }
15064
15112
 
15065
15113
  function useOAuthLogin() {
15114
+ const { handleSuccess, handleError } = useWidgetConfig();
15066
15115
  const [isLoading, setIsLoading] = React.useState(false);
15067
15116
  const [error, setError] = React.useState(null);
15068
15117
  const [isSuccess, setIsSuccess] = React.useState(false);
@@ -15078,15 +15127,26 @@ function useOAuthLogin() {
15078
15127
  setIsSuccess(true);
15079
15128
  setResult(oauthResult);
15080
15129
  setIsLoading(false);
15130
+ handleSuccess({
15131
+ method: 'oauth',
15132
+ didToken: oauthResult.magic.idToken,
15133
+ oauth: {
15134
+ provider: oauthResult.oauth.provider,
15135
+ name: oauthResult.oauth.userInfo.name,
15136
+ email: oauthResult.oauth.userInfo.email,
15137
+ picture: oauthResult.oauth.userInfo.picture,
15138
+ },
15139
+ });
15081
15140
  return oauthResult;
15082
15141
  }
15083
15142
  catch (err) {
15084
15143
  const errorInstance = err instanceof Error ? err : new Error('OAuth login failed');
15085
15144
  setError(errorInstance);
15086
15145
  setIsLoading(false);
15146
+ handleError(errorInstance);
15087
15147
  throw errorInstance;
15088
15148
  }
15089
- }, []);
15149
+ }, [handleSuccess, handleError]);
15090
15150
  return {
15091
15151
  performOAuthLogin,
15092
15152
  isLoading,
@@ -15371,33 +15431,86 @@ function WidgetContent({ state, dispatch }) {
15371
15431
  renderView(),
15372
15432
  React.createElement(c$5, null)))));
15373
15433
  }
15374
- // Main widget component - no props needed, everything is internal
15375
- function MagicWidget() {
15434
+ // Styles for modal mode
15435
+ const modalBackdropStyles = {
15436
+ position: 'fixed',
15437
+ top: 0,
15438
+ left: 0,
15439
+ right: 0,
15440
+ bottom: 0,
15441
+ backdropFilter: 'blur(0.375rem)',
15442
+ WebkitBackdropFilter: 'blur(0.375rem)', // Safari support
15443
+ display: 'flex',
15444
+ alignItems: 'flex-start',
15445
+ justifyContent: 'center',
15446
+ paddingTop: '15vh', // Slightly above center
15447
+ zIndex: 9999,
15448
+ };
15449
+ const modalContentStyles = {
15450
+ position: 'relative',
15451
+ };
15452
+ // Main widget component
15453
+ function MagicWidget({ displayMode = 'inline', isOpen = true, onClose, closeOnSuccess = false, closeOnClickOutside = false, wallets = [], onSuccess, onError, }) {
15376
15454
  const [state, dispatch] = React.useReducer(widgetReducer, initialState);
15377
- const [isConfigLoading, setIsConfigLoading] = React.useState(true);
15455
+ // Check if config is already cached to avoid unnecessary loading state
15456
+ const [isConfigLoading, setIsConfigLoading] = React.useState(() => {
15457
+ return getExtensionInstance().getConfig() === null;
15458
+ });
15378
15459
  React.useEffect(() => {
15379
15460
  injectCSS();
15380
- getExtensionInstance()
15381
- .fetchConfig()
15382
- .then(() => setIsConfigLoading(false))
15383
- .catch(err => {
15384
- console.error('Failed to fetch config:', err);
15385
- setIsConfigLoading(false); // Still show widget on error
15386
- });
15387
- }, []);
15461
+ // Only fetch if not already cached
15462
+ if (isConfigLoading) {
15463
+ getExtensionInstance()
15464
+ .fetchConfig()
15465
+ .then(() => setIsConfigLoading(false))
15466
+ .catch(err => {
15467
+ console.error('Failed to fetch config:', err);
15468
+ setIsConfigLoading(false); // Still show widget on error
15469
+ });
15470
+ }
15471
+ }, [isConfigLoading]);
15472
+ // Reset to login view when modal is opened
15473
+ React.useEffect(() => {
15474
+ if (isOpen) {
15475
+ dispatch({ type: 'GO_TO_LOGIN' });
15476
+ }
15477
+ }, [isOpen]);
15478
+ if (!isOpen) {
15479
+ return null;
15480
+ }
15481
+ const isModal = displayMode === 'modal';
15482
+ // Handle backdrop click for closeOnClickOutside
15483
+ const handleBackdropClick = (e) => {
15484
+ // Only close if clicking the backdrop itself, not the content
15485
+ if (closeOnClickOutside && e.target === e.currentTarget && onClose) {
15486
+ onClose();
15487
+ }
15488
+ };
15388
15489
  if (isConfigLoading) {
15389
- return (React.createElement(r$3, null,
15490
+ const loadingContent = (React.createElement(r$3, null,
15390
15491
  React.createElement(VStack, { alignItems: "center", justifyContent: "center", height: "300px" },
15391
15492
  React.createElement(d$m, null))));
15493
+ if (isModal) {
15494
+ return (React.createElement("div", { style: modalBackdropStyles, onClick: handleBackdropClick },
15495
+ React.createElement("div", { style: modalContentStyles }, loadingContent)));
15496
+ }
15497
+ return loadingContent;
15498
+ }
15499
+ const widgetContent = (React.createElement(WidgetConfigProvider, { wallets: wallets, onSuccess: onSuccess, onError: onError, onClose: onClose, closeOnSuccess: closeOnSuccess },
15500
+ React.createElement(wagmi.WagmiProvider, { config: wagmiConfig },
15501
+ React.createElement(reactQuery.QueryClientProvider, { client: queryClient },
15502
+ React.createElement("div", { id: "magic-widget-container" },
15503
+ React.createElement(WidgetContent, { state: state, dispatch: dispatch }))))));
15504
+ if (isModal) {
15505
+ return (React.createElement("div", { style: modalBackdropStyles, onClick: handleBackdropClick },
15506
+ React.createElement("div", { style: modalContentStyles }, widgetContent)));
15392
15507
  }
15393
- return (React.createElement(wagmi.WagmiProvider, { config: wagmiConfig },
15394
- React.createElement(reactQuery.QueryClientProvider, { client: queryClient },
15395
- React.createElement("div", { id: "magic-widget-container" },
15396
- React.createElement(WidgetContent, { state: state, dispatch: dispatch })))));
15508
+ return widgetContent;
15397
15509
  }
15398
15510
 
15399
15511
  exports.MAGIC_WIDGET_PROVIDER = MAGIC_WIDGET_PROVIDER;
15400
15512
  exports.MagicWidget = MagicWidget;
15401
15513
  exports.MagicWidgetExtension = MagicWidgetExtension;
15514
+ exports.ThirdPartyWallets = ThirdPartyWallets;
15402
15515
  exports.getExtensionInstance = getExtensionInstance;
15403
15516
  //# sourceMappingURL=index.js.map