@volr/react-ui 0.1.119 → 0.1.121

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/index.cjs CHANGED
@@ -531,6 +531,7 @@ var en = {
531
531
  titleTouchId: "Set up Touch ID login",
532
532
  titleFaceId: "Set up Face ID login",
533
533
  titleFingerprint: "Set up fingerprint login",
534
+ titleQrCode: "Set up mobile login",
534
535
  // Short description
535
536
  description: "Sign in instantly, every time.",
536
537
  // Legacy keys for backward compatibility
@@ -560,6 +561,7 @@ var en = {
560
561
  outdatedSamsung: "Please update Samsung Internet to the latest version.",
561
562
  outdatedIOS: "iOS 17.4 or later is required. Please check for software updates in Settings.",
562
563
  firefoxNotSupported: "Secure login is not available in Firefox. Please use Chrome or Safari.",
564
+ whaleNotSupported: "Secure login is not available in Whale browser. Please use Chrome.",
563
565
  unknownBrowser: "Secure login may not work properly in this browser.",
564
566
  openInExternalBrowser: "Open in external browser",
565
567
  updateBrowser: "Update browser",
@@ -573,6 +575,19 @@ var en = {
573
575
  windows: "Please use your phone via QR code. (Windows Hello is not supported)",
574
576
  default: "Please use this device's biometric.",
575
577
  note: "Using other devices or apps may not work."
578
+ },
579
+ migration: {
580
+ title: "Set up passkey for this site",
581
+ description: "Your wallet was created on {{sourceDomain}}. To use it here, you need to set up a new passkey for this site.",
582
+ descriptionGeneric: "Your wallet was created on a different site. To use it here, you need to set up a new passkey for this site.",
583
+ currentDomain: "Current site",
584
+ sourceDomain: "Original site",
585
+ benefits: "Your wallet address and balance will remain the same.",
586
+ cta: "Set up passkey",
587
+ later: "Do it later",
588
+ inProgress: "Setting up...",
589
+ success: "Passkey set up successfully!",
590
+ error: "Failed to set up passkey. Please try again."
576
591
  }
577
592
  },
578
593
  success: {
@@ -758,6 +773,7 @@ var ko = {
758
773
  titleTouchId: "Touch ID \uB85C\uADF8\uC778 \uC124\uC815",
759
774
  titleFaceId: "Face ID \uB85C\uADF8\uC778 \uC124\uC815",
760
775
  titleFingerprint: "\uC9C0\uBB38 \uB85C\uADF8\uC778 \uC124\uC815",
776
+ titleQrCode: "\uD734\uB300\uD3F0\uC73C\uB85C \uB85C\uADF8\uC778 \uC124\uC815",
761
777
  // Short description
762
778
  description: "\uD55C \uBC88 \uB4F1\uB85D\uD558\uBA74, \uB2E4\uC74C\uBD80\uD130 \uBC14\uB85C \uB85C\uADF8\uC778",
763
779
  // Legacy keys for backward compatibility
@@ -787,6 +803,7 @@ var ko = {
787
803
  outdatedSamsung: "Samsung Internet\uC744 \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD574\uC8FC\uC138\uC694.",
788
804
  outdatedIOS: "iOS 17.4 \uC774\uC0C1 \uBC84\uC804\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uC124\uC815\uC5D0\uC11C \uC18C\uD504\uD2B8\uC6E8\uC5B4 \uC5C5\uB370\uC774\uD2B8\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.",
789
805
  firefoxNotSupported: "Firefox\uC5D0\uC11C\uB294 \uBCF4\uC548 \uB85C\uADF8\uC778\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. Chrome \uB610\uB294 Safari\uB97C \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.",
806
+ whaleNotSupported: "\uC6E8\uC77C \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 \uBCF4\uC548 \uB85C\uADF8\uC778\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. Chrome\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.",
790
807
  unknownBrowser: "\uC774 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C\uB294 \uBCF4\uC548 \uB85C\uADF8\uC778\uC774 \uC81C\uB300\uB85C \uC791\uB3D9\uD558\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
791
808
  openInExternalBrowser: "\uC678\uBD80 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC5F4\uAE30",
792
809
  updateBrowser: "\uBE0C\uB77C\uC6B0\uC800 \uC5C5\uB370\uC774\uD2B8",
@@ -800,6 +817,19 @@ var ko = {
800
817
  windows: "QR \uCF54\uB4DC\uB85C \uD734\uB300\uD3F0\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694. (Windows Hello\uB294 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)",
801
818
  default: "\uC774 \uAE30\uAE30\uC758 \uC0DD\uCCB4 \uC778\uC99D\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.",
802
819
  note: "\uB2E4\uB978 \uAE30\uAE30\uB098 \uC571 \uC0AC\uC6A9 \uC2DC \uB3D9\uC791\uD558\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
820
+ },
821
+ migration: {
822
+ title: "\uC774 \uC0AC\uC774\uD2B8\uC6A9 \uD328\uC2A4\uD0A4 \uC124\uC815",
823
+ description: "\uC9C0\uAC11\uC774 {{sourceDomain}}\uC5D0\uC11C \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 \uC0AC\uC774\uD2B8\uC5D0\uC11C \uC0AC\uC6A9\uD558\uB824\uBA74 \uC0C8 \uD328\uC2A4\uD0A4\uB97C \uC124\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.",
824
+ descriptionGeneric: "\uC9C0\uAC11\uC774 \uB2E4\uB978 \uC0AC\uC774\uD2B8\uC5D0\uC11C \uC0DD\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC774 \uC0AC\uC774\uD2B8\uC5D0\uC11C \uC0AC\uC6A9\uD558\uB824\uBA74 \uC0C8 \uD328\uC2A4\uD0A4\uB97C \uC124\uC815\uD574\uC57C \uD569\uB2C8\uB2E4.",
825
+ currentDomain: "\uD604\uC7AC \uC0AC\uC774\uD2B8",
826
+ sourceDomain: "\uC6D0\uBCF8 \uC0AC\uC774\uD2B8",
827
+ benefits: "\uC9C0\uAC11 \uC8FC\uC18C\uC640 \uC794\uC561\uC740 \uADF8\uB300\uB85C \uC720\uC9C0\uB429\uB2C8\uB2E4.",
828
+ cta: "\uD328\uC2A4\uD0A4 \uC124\uC815\uD558\uAE30",
829
+ later: "\uB098\uC911\uC5D0 \uD558\uAE30",
830
+ inProgress: "\uC124\uC815 \uC911...",
831
+ success: "\uD328\uC2A4\uD0A4 \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
832
+ error: "\uD328\uC2A4\uD0A4 \uC124\uC815\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694."
803
833
  }
804
834
  },
805
835
  success: {
@@ -1371,13 +1401,21 @@ var ModalHeader = ({
1371
1401
 
1372
1402
  // src/components/features/passkey/utils/biometric.ts
1373
1403
  function getBiometricType() {
1404
+ if (typeof navigator === "undefined") return "fingerprint";
1374
1405
  const ua = navigator.userAgent;
1406
+ const platform = navigator.platform || "";
1375
1407
  if (/iPhone|iPad|iPod/.test(ua)) {
1376
1408
  return "faceId";
1377
1409
  }
1378
1410
  if (/Macintosh|MacIntel/.test(ua)) {
1411
+ if (navigator.maxTouchPoints > 1) {
1412
+ return "faceId";
1413
+ }
1379
1414
  return "touchId";
1380
1415
  }
1416
+ if (/Win/.test(platform)) {
1417
+ return "qrCode";
1418
+ }
1381
1419
  return "fingerprint";
1382
1420
  }
1383
1421
  function getUserFriendlyError(error, t) {
@@ -1625,12 +1663,44 @@ function FingerprintIcon({ size }) {
1625
1663
  }
1626
1664
  );
1627
1665
  }
1666
+ function QrCodeIcon({ size }) {
1667
+ const fill = "#f87171";
1668
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1669
+ "svg",
1670
+ {
1671
+ width: size,
1672
+ height: size,
1673
+ viewBox: "0 0 24 24",
1674
+ fill: "none",
1675
+ xmlns: "http://www.w3.org/2000/svg",
1676
+ children: [
1677
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "7", height: "7", rx: "1", fill }),
1678
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "3", width: "7", height: "7", rx: "1", fill }),
1679
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1", fill }),
1680
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "5", width: "3", height: "3", fill: "white" }),
1681
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "16", y: "5", width: "3", height: "3", fill: "white" }),
1682
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "16", width: "3", height: "3", fill: "white" }),
1683
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "14", width: "2", height: "2", fill }),
1684
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "17", y: "14", width: "2", height: "2", fill }),
1685
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "17", width: "2", height: "2", fill }),
1686
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "19", y: "17", width: "2", height: "2", fill }),
1687
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "17", y: "19", width: "2", height: "2", fill }),
1688
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", y: "3", width: "2", height: "2", fill }),
1689
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", y: "6", width: "2", height: "2", fill }),
1690
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "11", y: "11", width: "2", height: "2", fill }),
1691
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "11", width: "2", height: "2", fill }),
1692
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "11", width: "2", height: "2", fill })
1693
+ ]
1694
+ }
1695
+ );
1696
+ }
1628
1697
  function BiometricIcon({ type, size = 80, animate = false }) {
1629
1698
  const className = animate ? "volr:animate-pulse" : "";
1630
1699
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
1631
1700
  type === "faceId" && /* @__PURE__ */ jsxRuntime.jsx(FaceIdIcon, { size }),
1632
1701
  type === "touchId" && /* @__PURE__ */ jsxRuntime.jsx(TouchIdIcon, { size }),
1633
- type === "fingerprint" && /* @__PURE__ */ jsxRuntime.jsx(FingerprintIcon, { size })
1702
+ type === "fingerprint" && /* @__PURE__ */ jsxRuntime.jsx(FingerprintIcon, { size }),
1703
+ type === "qrCode" && /* @__PURE__ */ jsxRuntime.jsx(QrCodeIcon, { size })
1634
1704
  ] });
1635
1705
  }
1636
1706
  var sizeMap = {
@@ -1819,6 +1889,84 @@ function PasskeyCompatibilityScreen({
1819
1889
  ] })
1820
1890
  ] });
1821
1891
  }
1892
+ function PasskeyMigrationView({
1893
+ sourcePasskey,
1894
+ currentDomain,
1895
+ onMigrate,
1896
+ onSkip,
1897
+ onError,
1898
+ isOpen = true,
1899
+ wrapInModal = true
1900
+ }) {
1901
+ const { t } = useI18n();
1902
+ const [isMigrating, setIsMigrating] = React13.useState(false);
1903
+ const [error, setError] = React13.useState(null);
1904
+ const biometricType = getBiometricType();
1905
+ const handleMigrate = async () => {
1906
+ try {
1907
+ setIsMigrating(true);
1908
+ setError(null);
1909
+ await onMigrate();
1910
+ } catch (err) {
1911
+ const errorMessage = err instanceof Error ? err.message : String(err);
1912
+ setError(t("passkey.migration.error"));
1913
+ if (onError) {
1914
+ onError(err instanceof Error ? err : new Error(errorMessage));
1915
+ }
1916
+ } finally {
1917
+ setIsMigrating(false);
1918
+ }
1919
+ };
1920
+ const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1921
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "volr:text-xl volr:font-semibold volr:mb-4", children: t("passkey.migration.title") }),
1922
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "volr:my-6 volr:flex volr:justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(BiometricIcon, { type: biometricType, size: 48 }) }),
1923
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "volr:text-sm volr:mb-4 volr:text-center volr-text-secondary", children: sourcePasskey.rpId ? t("passkey.migration.description").replace(
1924
+ "{{sourceDomain}}",
1925
+ sourcePasskey.rpId
1926
+ ) : t("passkey.migration.descriptionGeneric") }),
1927
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "volr:mb-4 volr:p-3 volr:rounded-lg volr:border volr:border-slate-200 volr:bg-slate-50", children: [
1928
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "volr:flex volr:justify-between volr:items-center volr:text-sm volr:mb-2", children: [
1929
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "volr-text-secondary", children: t("passkey.migration.sourceDomain") }),
1930
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "volr:font-mono volr:text-xs", children: sourcePasskey.rpId })
1931
+ ] }),
1932
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "volr:flex volr:justify-between volr:items-center volr:text-sm", children: [
1933
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "volr-text-secondary", children: t("passkey.migration.currentDomain") }),
1934
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "volr:font-mono volr:text-xs", children: currentDomain })
1935
+ ] })
1936
+ ] }),
1937
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "volr:mb-6 volr:p-3 volr:rounded-lg volr-hint", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "volr:text-sm volr:flex volr:items-start volr:gap-2", children: [
1938
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "volr:text-base", children: "\u2713" }),
1939
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("passkey.migration.benefits") })
1940
+ ] }) }),
1941
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "volr:mb-4 volr:p-3 volr:rounded-lg volr:border volr:text-sm volr:text-left volr-error", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }) }),
1942
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "volr:flex volr:flex-col volr:gap-3", children: [
1943
+ /* @__PURE__ */ jsxRuntime.jsx(
1944
+ Button,
1945
+ {
1946
+ variant: "primary",
1947
+ fullWidth: true,
1948
+ onClick: handleMigrate,
1949
+ disabled: isMigrating,
1950
+ children: isMigrating ? t("passkey.migration.inProgress") : t("passkey.migration.cta")
1951
+ }
1952
+ ),
1953
+ onSkip && /* @__PURE__ */ jsxRuntime.jsx(
1954
+ Button,
1955
+ {
1956
+ variant: "ghost",
1957
+ fullWidth: true,
1958
+ onClick: onSkip,
1959
+ disabled: isMigrating,
1960
+ children: t("passkey.migration.later")
1961
+ }
1962
+ )
1963
+ ] })
1964
+ ] });
1965
+ if (!wrapInModal) {
1966
+ return content;
1967
+ }
1968
+ return /* @__PURE__ */ jsxRuntime.jsx(Modal, { open: isOpen, onOpenChange: (open) => !open && onSkip?.(), children: content });
1969
+ }
1822
1970
  function PasskeyEnrollView({
1823
1971
  onComplete,
1824
1972
  onError,
@@ -1847,14 +1995,34 @@ function PasskeyEnrollView({
1847
1995
  [compatibility.platform]
1848
1996
  );
1849
1997
  const hasPasskey = user?.keyStorageType === "passkey";
1998
+ const currentDomain = React13.useMemo(() => {
1999
+ if (typeof window === "undefined") return "localhost";
2000
+ return window.location.hostname;
2001
+ }, []);
2002
+ const migrationInfo = React13.useMemo(() => {
2003
+ if (!user?.registeredPasskeys || user.registeredPasskeys.length === 0) {
2004
+ return { needsMigration: false, sourcePasskey: null };
2005
+ }
2006
+ const hasPasskeyOnCurrentDomain = user.registeredPasskeys.some(
2007
+ (pk) => pk.rpId === currentDomain
2008
+ );
2009
+ if (hasPasskeyOnCurrentDomain) {
2010
+ return { needsMigration: false, sourcePasskey: null };
2011
+ }
2012
+ const sourcePasskey = user.registeredPasskeys[0];
2013
+ return { needsMigration: true, sourcePasskey };
2014
+ }, [user?.registeredPasskeys, currentDomain]);
1850
2015
  React13.useEffect(() => {
1851
2016
  console.log("[PasskeyEnrollView] User state:", {
1852
2017
  user,
1853
2018
  keyStorageType: user?.keyStorageType,
1854
2019
  evmAddress: user?.evmAddress,
1855
- hasPasskey
2020
+ hasPasskey,
2021
+ registeredPasskeys: user?.registeredPasskeys,
2022
+ currentDomain,
2023
+ migrationInfo
1856
2024
  });
1857
- }, [user, hasPasskey]);
2025
+ }, [user, hasPasskey, currentDomain, migrationInfo]);
1858
2026
  React13.useEffect(() => {
1859
2027
  if (hasPasskey && !user?.evmAddress && !isRefreshing) {
1860
2028
  const refreshUserData = async () => {
@@ -1965,13 +2133,16 @@ function PasskeyEnrollView({
1965
2133
  }
1966
2134
  };
1967
2135
  const getBiometricTitle = () => {
1968
- if (biometricType === "faceId") {
1969
- return t("passkey.titleFaceId");
1970
- }
1971
- if (biometricType === "touchId") {
1972
- return t("passkey.titleTouchId");
2136
+ switch (biometricType) {
2137
+ case "faceId":
2138
+ return t("passkey.titleFaceId");
2139
+ case "touchId":
2140
+ return t("passkey.titleTouchId");
2141
+ case "qrCode":
2142
+ return t("passkey.titleQrCode");
2143
+ default:
2144
+ return t("passkey.titleFingerprint");
1973
2145
  }
1974
- return t("passkey.titleFingerprint");
1975
2146
  };
1976
2147
  const getBiometricDescription = () => {
1977
2148
  return t("passkey.description");
@@ -1989,6 +2160,23 @@ function PasskeyEnrollView({
1989
2160
  }
1990
2161
  return /* @__PURE__ */ jsxRuntime.jsx(Modal, { open: isOpen, onOpenChange: (open) => !open && onLogout?.(), children: compatibilityContent });
1991
2162
  }
2163
+ if (migrationInfo.needsMigration && migrationInfo.sourcePasskey) {
2164
+ const handleMigration = async () => {
2165
+ await handleEnroll();
2166
+ };
2167
+ return /* @__PURE__ */ jsxRuntime.jsx(
2168
+ PasskeyMigrationView,
2169
+ {
2170
+ sourcePasskey: migrationInfo.sourcePasskey,
2171
+ currentDomain,
2172
+ onMigrate: handleMigration,
2173
+ onSkip: handleLogout,
2174
+ onError,
2175
+ isOpen,
2176
+ wrapInModal
2177
+ }
2178
+ );
2179
+ }
1992
2180
  if (hasPasskey) {
1993
2181
  const handleClose = () => {
1994
2182
  if (onClose) {