@mezo-org/passport 0.15.1-dev.7 → 0.16.0-dev.1

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.
Files changed (170) hide show
  1. package/dist/src/api/auth.d.ts +9 -0
  2. package/dist/src/api/auth.d.ts.map +1 -1
  3. package/dist/src/api/auth.js +7 -0
  4. package/dist/src/api/auth.js.map +1 -1
  5. package/dist/src/assets/DefaultAvatar.d.ts +5 -0
  6. package/dist/src/assets/DefaultAvatar.d.ts.map +1 -0
  7. package/dist/src/assets/DefaultAvatar.js +21 -0
  8. package/dist/src/assets/DefaultAvatar.js.map +1 -0
  9. package/dist/src/assets/EditIcon.d.ts +5 -0
  10. package/dist/src/assets/EditIcon.d.ts.map +1 -0
  11. package/dist/src/assets/EditIcon.js +10 -0
  12. package/dist/src/assets/EditIcon.js.map +1 -0
  13. package/dist/src/components/Dropdown/ConnectedTrigger.d.ts +8 -0
  14. package/dist/src/components/Dropdown/ConnectedTrigger.d.ts.map +1 -0
  15. package/dist/src/components/Dropdown/ConnectedTrigger.js +39 -0
  16. package/dist/src/components/Dropdown/ConnectedTrigger.js.map +1 -0
  17. package/dist/src/components/Dropdown/Content.d.ts +8 -0
  18. package/dist/src/components/Dropdown/Content.d.ts.map +1 -0
  19. package/dist/src/components/Dropdown/Content.js +29 -0
  20. package/dist/src/components/Dropdown/Content.js.map +1 -0
  21. package/dist/src/components/Dropdown/DisconnectedTrigger.d.ts +7 -0
  22. package/dist/src/components/Dropdown/DisconnectedTrigger.d.ts.map +1 -0
  23. package/dist/src/components/Dropdown/DisconnectedTrigger.js +13 -0
  24. package/dist/src/components/Dropdown/DisconnectedTrigger.js.map +1 -0
  25. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -0
  26. package/dist/src/components/Dropdown/Dropdown.d.ts.map +1 -0
  27. package/dist/src/components/Dropdown/Dropdown.js +45 -0
  28. package/dist/src/components/Dropdown/Dropdown.js.map +1 -0
  29. package/dist/src/components/Dropdown/ListingItem.d.ts +14 -0
  30. package/dist/src/components/Dropdown/ListingItem.d.ts.map +1 -0
  31. package/dist/src/components/Dropdown/ListingItem.js +42 -0
  32. package/dist/src/components/Dropdown/ListingItem.js.map +1 -0
  33. package/dist/src/components/Dropdown/NestedViewLayout.d.ts +8 -0
  34. package/dist/src/components/Dropdown/NestedViewLayout.d.ts.map +1 -0
  35. package/dist/src/components/Dropdown/NestedViewLayout.js +33 -0
  36. package/dist/src/components/Dropdown/NestedViewLayout.js.map +1 -0
  37. package/dist/src/components/Dropdown/Receive/Receive.d.ts +4 -0
  38. package/dist/src/components/Dropdown/Receive/Receive.d.ts.map +1 -0
  39. package/dist/src/components/Dropdown/Receive/Receive.js +64 -0
  40. package/dist/src/components/Dropdown/Receive/Receive.js.map +1 -0
  41. package/dist/src/components/Dropdown/Root/AccountAddressActions.d.ts +4 -0
  42. package/dist/src/components/Dropdown/Root/AccountAddressActions.d.ts.map +1 -0
  43. package/dist/src/components/Dropdown/Root/AccountAddressActions.js +49 -0
  44. package/dist/src/components/Dropdown/Root/AccountAddressActions.js.map +1 -0
  45. package/dist/src/components/Dropdown/Root/AccountBalance.d.ts +6 -0
  46. package/dist/src/components/Dropdown/Root/AccountBalance.d.ts.map +1 -0
  47. package/dist/src/components/Dropdown/Root/AccountBalance.js +32 -0
  48. package/dist/src/components/Dropdown/Root/AccountBalance.js.map +1 -0
  49. package/dist/src/components/Dropdown/Root/AccountBtcListing.d.ts +6 -0
  50. package/dist/src/components/Dropdown/Root/AccountBtcListing.d.ts.map +1 -0
  51. package/dist/src/components/Dropdown/Root/AccountBtcListing.js +28 -0
  52. package/dist/src/components/Dropdown/Root/AccountBtcListing.js.map +1 -0
  53. package/dist/src/components/Dropdown/Root/AccountError.d.ts +8 -0
  54. package/dist/src/components/Dropdown/Root/AccountError.d.ts.map +1 -0
  55. package/dist/src/components/Dropdown/Root/AccountError.js +17 -0
  56. package/dist/src/components/Dropdown/Root/AccountError.js.map +1 -0
  57. package/dist/src/components/Dropdown/Root/AccountMusdListing.d.ts +4 -0
  58. package/dist/src/components/Dropdown/Root/AccountMusdListing.d.ts.map +1 -0
  59. package/dist/src/components/Dropdown/Root/AccountMusdListing.js +21 -0
  60. package/dist/src/components/Dropdown/Root/AccountMusdListing.js.map +1 -0
  61. package/dist/src/components/Dropdown/Root/AccountOtherAssets.d.ts +8 -0
  62. package/dist/src/components/Dropdown/Root/AccountOtherAssets.d.ts.map +1 -0
  63. package/dist/src/components/Dropdown/Root/AccountOtherAssets.js +43 -0
  64. package/dist/src/components/Dropdown/Root/AccountOtherAssets.js.map +1 -0
  65. package/dist/src/components/Dropdown/Root/Root.d.ts +8 -0
  66. package/dist/src/components/Dropdown/Root/Root.d.ts.map +1 -0
  67. package/dist/src/components/Dropdown/Root/Root.js +49 -0
  68. package/dist/src/components/Dropdown/Root/Root.js.map +1 -0
  69. package/dist/src/components/Dropdown/Root/WalletAddress.d.ts +4 -0
  70. package/dist/src/components/Dropdown/Root/WalletAddress.d.ts.map +1 -0
  71. package/dist/src/components/Dropdown/Root/WalletAddress.js +66 -0
  72. package/dist/src/components/Dropdown/Root/WalletAddress.js.map +1 -0
  73. package/dist/src/components/Dropdown/Root/WelcomeBlock.d.ts +6 -0
  74. package/dist/src/components/Dropdown/Root/WelcomeBlock.d.ts.map +1 -0
  75. package/dist/src/components/Dropdown/Root/WelcomeBlock.js +88 -0
  76. package/dist/src/components/Dropdown/Root/WelcomeBlock.js.map +1 -0
  77. package/dist/src/components/Dropdown/Settings/InlineEditField.d.ts +12 -0
  78. package/dist/src/components/Dropdown/Settings/InlineEditField.d.ts.map +1 -0
  79. package/dist/src/components/Dropdown/Settings/InlineEditField.js +95 -0
  80. package/dist/src/components/Dropdown/Settings/InlineEditField.js.map +1 -0
  81. package/dist/src/components/Dropdown/Settings/Settings.d.ts +4 -0
  82. package/dist/src/components/Dropdown/Settings/Settings.d.ts.map +1 -0
  83. package/dist/src/components/Dropdown/Settings/Settings.js +36 -0
  84. package/dist/src/components/Dropdown/Settings/Settings.js.map +1 -0
  85. package/dist/src/components/Dropdown/SlotNumber.d.ts +19 -0
  86. package/dist/src/components/Dropdown/SlotNumber.d.ts.map +1 -0
  87. package/dist/src/components/Dropdown/SlotNumber.js +67 -0
  88. package/dist/src/components/Dropdown/SlotNumber.js.map +1 -0
  89. package/dist/src/components/Dropdown/TestnetTopBanner.d.ts +3 -0
  90. package/dist/src/components/Dropdown/TestnetTopBanner.d.ts.map +1 -0
  91. package/dist/src/components/Dropdown/TestnetTopBanner.js +14 -0
  92. package/dist/src/components/Dropdown/TestnetTopBanner.js.map +1 -0
  93. package/dist/src/components/Dropdown/index.d.ts +3 -0
  94. package/dist/src/components/Dropdown/index.d.ts.map +1 -0
  95. package/dist/src/components/Dropdown/index.js +2 -0
  96. package/dist/src/components/Dropdown/index.js.map +1 -0
  97. package/dist/src/components/index.d.ts +2 -0
  98. package/dist/src/components/index.d.ts.map +1 -0
  99. package/dist/src/components/index.js +2 -0
  100. package/dist/src/components/index.js.map +1 -0
  101. package/dist/src/hooks/index.d.ts +1 -0
  102. package/dist/src/hooks/index.d.ts.map +1 -1
  103. package/dist/src/hooks/index.js +1 -0
  104. package/dist/src/hooks/index.js.map +1 -1
  105. package/dist/src/hooks/useAcceptDocuments.d.ts +18 -0
  106. package/dist/src/hooks/useAcceptDocuments.d.ts.map +1 -1
  107. package/dist/src/hooks/useAuthenticateWithWallet.d.ts +11 -11
  108. package/dist/src/hooks/useCreateAccount.d.ts +18 -0
  109. package/dist/src/hooks/useCreateAccount.d.ts.map +1 -1
  110. package/dist/src/hooks/useSignInWithWallet.d.ts +11 -11
  111. package/dist/src/hooks/useSignUpWithWallet.d.ts +11 -11
  112. package/dist/src/hooks/useUpdateMezoId.d.ts +18 -0
  113. package/dist/src/hooks/useUpdateMezoId.d.ts.map +1 -1
  114. package/dist/src/hooks/useUpdateUserProfile.d.ts +171 -0
  115. package/dist/src/hooks/useUpdateUserProfile.d.ts.map +1 -0
  116. package/dist/src/hooks/useUpdateUserProfile.js +19 -0
  117. package/dist/src/hooks/useUpdateUserProfile.js.map +1 -0
  118. package/dist/src/hooks/useWalletAccount.d.ts +2 -5
  119. package/dist/src/hooks/useWalletAccount.d.ts.map +1 -1
  120. package/dist/src/hooks/useWalletAccount.js.map +1 -1
  121. package/dist/src/index.d.ts +1 -0
  122. package/dist/src/index.d.ts.map +1 -1
  123. package/dist/src/index.js +1 -0
  124. package/dist/src/index.js.map +1 -1
  125. package/dist/src/stores/dropdownStore.d.ts +2 -1
  126. package/dist/src/stores/dropdownStore.d.ts.map +1 -1
  127. package/dist/src/stores/dropdownStore.js +1 -0
  128. package/dist/src/stores/dropdownStore.js.map +1 -1
  129. package/dist/src/utils/validation.d.ts +13 -0
  130. package/dist/src/utils/validation.d.ts.map +1 -0
  131. package/dist/src/utils/validation.js +34 -0
  132. package/dist/src/utils/validation.js.map +1 -0
  133. package/dist/src/utils/validation.test.d.ts +2 -0
  134. package/dist/src/utils/validation.test.d.ts.map +1 -0
  135. package/dist/src/utils/validation.test.js +63 -0
  136. package/dist/src/utils/validation.test.js.map +1 -0
  137. package/package.json +10 -7
  138. package/src/api/auth.ts +22 -0
  139. package/src/assets/DefaultAvatar.tsx +74 -0
  140. package/src/assets/EditIcon.tsx +33 -0
  141. package/src/components/Dropdown/ConnectedTrigger.tsx +76 -0
  142. package/src/components/Dropdown/Content.tsx +56 -0
  143. package/src/components/Dropdown/DisconnectedTrigger.tsx +36 -0
  144. package/src/components/Dropdown/Dropdown.tsx +100 -0
  145. package/src/components/Dropdown/ListingItem.tsx +176 -0
  146. package/src/components/Dropdown/NestedViewLayout.tsx +88 -0
  147. package/src/components/Dropdown/README.md +41 -0
  148. package/src/components/Dropdown/Receive/Receive.tsx +144 -0
  149. package/src/components/Dropdown/Root/AccountAddressActions.tsx +99 -0
  150. package/src/components/Dropdown/Root/AccountBalance.tsx +69 -0
  151. package/src/components/Dropdown/Root/AccountBtcListing.tsx +53 -0
  152. package/src/components/Dropdown/Root/AccountError.tsx +34 -0
  153. package/src/components/Dropdown/Root/AccountMusdListing.tsx +45 -0
  154. package/src/components/Dropdown/Root/AccountOtherAssets.tsx +85 -0
  155. package/src/components/Dropdown/Root/Root.tsx +104 -0
  156. package/src/components/Dropdown/Root/WalletAddress.tsx +123 -0
  157. package/src/components/Dropdown/Root/WelcomeBlock.tsx +173 -0
  158. package/src/components/Dropdown/Settings/InlineEditField.tsx +212 -0
  159. package/src/components/Dropdown/Settings/Settings.tsx +87 -0
  160. package/src/components/Dropdown/SlotNumber.tsx +131 -0
  161. package/src/components/Dropdown/TestnetTopBanner.tsx +32 -0
  162. package/src/components/Dropdown/index.ts +2 -0
  163. package/src/components/index.ts +1 -0
  164. package/src/hooks/index.ts +1 -0
  165. package/src/hooks/useUpdateUserProfile.ts +34 -0
  166. package/src/hooks/useWalletAccount.ts +2 -8
  167. package/src/index.ts +1 -0
  168. package/src/stores/dropdownStore.ts +1 -0
  169. package/src/utils/validation.test.ts +97 -0
  170. package/src/utils/validation.ts +35 -0
@@ -0,0 +1,104 @@
1
+ import {
2
+ Block,
3
+ Button,
4
+ LogOut01,
5
+ Settings01,
6
+ useStyletron,
7
+ } from "@mezo-org/mezo-clay"
8
+ import React, { useCallback } from "react"
9
+ import { useDisconnect } from "wagmi"
10
+ import { useSignOut } from "../../../hooks"
11
+ import WalletAddress from "./WalletAddress"
12
+ import AccountBalance from "./AccountBalance"
13
+ import WelcomeBlock from "./WelcomeBlock"
14
+ import AccountAddressActions from "./AccountAddressActions"
15
+ import AccountOtherAssets from "./AccountOtherAssets"
16
+ import AccountBtcListing from "./AccountBtcListing"
17
+ import AccountMusdListing from "./AccountMusdListing"
18
+ import useDropdownStore, { DropdownView } from "../../../stores/dropdownStore"
19
+
20
+ type RootProps = {
21
+ onSignOut?: () => void
22
+ onOtherAssetsClick?: () => void
23
+ }
24
+
25
+ export default function Root(props: RootProps) {
26
+ const { onSignOut, onOtherAssetsClick } = props
27
+
28
+ const [, theme] = useStyletron()
29
+
30
+ const { disconnect } = useDisconnect()
31
+ const { signOut } = useSignOut()
32
+
33
+ const setDropdownView = useDropdownStore((state) => state.setView)
34
+ const handleSettingsClick = useCallback(
35
+ () => setDropdownView(DropdownView.SETTINGS),
36
+ [setDropdownView],
37
+ )
38
+
39
+ const handleLogOut = useCallback(() => {
40
+ if (onSignOut) {
41
+ onSignOut()
42
+ return
43
+ }
44
+ signOut()
45
+ disconnect()
46
+ }, [signOut, disconnect, onSignOut])
47
+
48
+ const overridesWithDivider = {
49
+ Block: {
50
+ style: {
51
+ borderBottomWidth: "1px",
52
+ borderBottomStyle: "solid",
53
+ borderBottomColor: theme.colors.borderOpaque,
54
+ },
55
+ },
56
+ }
57
+
58
+ return (
59
+ <Block display="inline-flex" flexDirection="column">
60
+ <WelcomeBlock />
61
+
62
+ <AccountAddressActions overrides={overridesWithDivider} />
63
+
64
+ <WalletAddress overrides={overridesWithDivider} />
65
+
66
+ <AccountBalance overrides={overridesWithDivider} />
67
+
68
+ <AccountBtcListing />
69
+
70
+ <AccountMusdListing />
71
+
72
+ <AccountOtherAssets
73
+ onClick={onOtherAssetsClick}
74
+ overrides={overridesWithDivider}
75
+ />
76
+
77
+ <Block
78
+ display="flex"
79
+ alignItems="center"
80
+ justifyContent="space-between"
81
+ padding={theme.sizing.scale300}
82
+ >
83
+ <Button
84
+ size="small"
85
+ shape="pill"
86
+ kind="tertiary"
87
+ startEnhancer={<Settings01 color="currentColor" size={16} />}
88
+ onClick={handleSettingsClick}
89
+ >
90
+ Settings
91
+ </Button>
92
+ <Button
93
+ size="small"
94
+ shape="pill"
95
+ kind="tertiary"
96
+ startEnhancer={<LogOut01 color="currentColor" size={16} />}
97
+ onClick={handleLogOut}
98
+ >
99
+ Sign out
100
+ </Button>
101
+ </Block>
102
+ </Block>
103
+ )
104
+ }
@@ -0,0 +1,123 @@
1
+ import {
2
+ BitcoinCircle,
3
+ Block,
4
+ BlockProps,
5
+ EthCircle,
6
+ LabelMedium,
7
+ LabelXSmall,
8
+ mergeOverrides,
9
+ MonoLabelXSmall,
10
+ useStyletron,
11
+ } from "@mezo-org/mezo-clay"
12
+ import React, { useState } from "react"
13
+ import { useCopyToClipboard } from "usehooks-ts"
14
+ import { usePassportContext } from "../../../hooks/usePassportContext"
15
+ import { getAddressExplorerUrl, trimAddress } from "../../../utils/address"
16
+ import { useWalletAccount } from "../../../hooks/useWalletAccount"
17
+
18
+ export default function WalletAddress(props: BlockProps) {
19
+ const { overrides, ...restProps } = props
20
+ const { walletAddress, networkFamily } = useWalletAccount()
21
+ const { environment = "mainnet" } = usePassportContext()
22
+ const [, theme] = useStyletron()
23
+ const [, copy] = useCopyToClipboard()
24
+ const [isCopiedMessageVisible, setIsCopiedMessageVisible] = useState(false)
25
+
26
+ const handleCopyAddress = () => {
27
+ copy(walletAddress!)
28
+ setIsCopiedMessageVisible(true)
29
+
30
+ setTimeout(() => {
31
+ setIsCopiedMessageVisible(false)
32
+ }, 2000)
33
+ }
34
+
35
+ const blockExplorerUrl = getAddressExplorerUrl(
36
+ walletAddress!,
37
+ networkFamily,
38
+ environment === "testnet",
39
+ )
40
+ const trimmedAddress = trimAddress(walletAddress!)
41
+ const AccountIcon = networkFamily === "bitcoin" ? BitcoinCircle : EthCircle
42
+
43
+ return (
44
+ <Block
45
+ as="button"
46
+ onClick={handleCopyAddress}
47
+ display="flex"
48
+ alignItems="center"
49
+ backgroundColor="transparent"
50
+ padding={`${theme.sizing.scale800} ${theme.sizing.scale700}`}
51
+ overrides={mergeOverrides(
52
+ {
53
+ Block: {
54
+ style: {
55
+ gap: theme.sizing.scale700,
56
+ borderWidth: 0,
57
+ cursor: "pointer",
58
+ ":hover": {
59
+ backgroundColor: theme.colors.backgroundTertiary,
60
+ },
61
+ },
62
+ },
63
+ },
64
+ overrides,
65
+ )}
66
+ {...restProps}
67
+ >
68
+ <AccountIcon
69
+ color={theme.colors.black}
70
+ size={theme.sizing.scale800}
71
+ overrides={{
72
+ Svg: {
73
+ style: {
74
+ backgroundColor: theme.colors.black,
75
+ overflow: "hidden",
76
+ borderRadius: theme.borders.radius200,
77
+ },
78
+ },
79
+ }}
80
+ />
81
+ <Block
82
+ display="flex"
83
+ flexDirection="column"
84
+ alignItems="flex-start"
85
+ overrides={{
86
+ Block: {
87
+ style: {
88
+ gap: theme.sizing.scale100,
89
+ },
90
+ },
91
+ }}
92
+ >
93
+ <LabelMedium>Connected Wallet</LabelMedium>
94
+
95
+ {isCopiedMessageVisible ? (
96
+ <LabelXSmall color={theme.colors.contentSecondary}>
97
+ Copied address to clipboard
98
+ </LabelXSmall>
99
+ ) : (
100
+ <Block
101
+ as="a"
102
+ color={theme.colors.contentSecondary}
103
+ href={blockExplorerUrl}
104
+ target="_blank"
105
+ overrides={{
106
+ Block: {
107
+ style: {
108
+ color: "inherit",
109
+ textDecoration: "none",
110
+ ":hover": {
111
+ textDecoration: "underline",
112
+ },
113
+ },
114
+ },
115
+ }}
116
+ >
117
+ <MonoLabelXSmall>{trimmedAddress}</MonoLabelXSmall>
118
+ </Block>
119
+ )}
120
+ </Block>
121
+ </Block>
122
+ )
123
+ }
@@ -0,0 +1,173 @@
1
+ import {
2
+ Block,
3
+ BlockProps,
4
+ HeadingMedium,
5
+ LabelLarge,
6
+ Mats,
7
+ mergeOverrides,
8
+ Skeleton,
9
+ useStyletron,
10
+ } from "@mezo-org/mezo-clay"
11
+ import React from "react"
12
+ import DefaultAvatar from "../../../assets/DefaultAvatar"
13
+ import { useGetCurrentAccount } from "../../../hooks"
14
+ import { formatNumberToCompactString } from "../../../utils/numbers"
15
+ import AccountError from "./AccountError"
16
+ import { usePassportContext } from "../../../hooks/usePassportContext"
17
+ import SlotNumber from "../SlotNumber"
18
+
19
+ const DOT_DELIMITER_REGEX = /(?=\.)/
20
+
21
+ function WelcomeBlockSkeleton() {
22
+ const [, theme] = useStyletron()
23
+
24
+ return (
25
+ <Block
26
+ display="flex"
27
+ alignItems="center"
28
+ padding={theme.sizing.scale600}
29
+ paddingTop={theme.sizing.scale900}
30
+ paddingBottom={theme.sizing.scale800}
31
+ overrides={{
32
+ Block: {
33
+ style: {
34
+ gap: theme.sizing.scale600,
35
+ },
36
+ },
37
+ }}
38
+ >
39
+ <Skeleton
40
+ animation
41
+ width={theme.sizing.scale1200}
42
+ height={theme.sizing.scale1200}
43
+ overrides={{
44
+ Root: {
45
+ style: {
46
+ borderRadius: "100%",
47
+ },
48
+ },
49
+ }}
50
+ />
51
+
52
+ <Block
53
+ display="flex"
54
+ flexDirection="column"
55
+ flex={1}
56
+ overrides={{
57
+ Block: {
58
+ style: {
59
+ gap: theme.sizing.scale100,
60
+ },
61
+ },
62
+ }}
63
+ >
64
+ <Skeleton animation width="60%" height={theme.sizing.scale900} />
65
+ <Skeleton animation width="40%" height={theme.sizing.scale800} />
66
+ </Block>
67
+ </Block>
68
+ )
69
+ }
70
+
71
+ type WelcomeBlockProps = BlockProps
72
+
73
+ export default function WelcomeBlock(props: WelcomeBlockProps) {
74
+ const { overrides, ...restProps } = props
75
+ const { accountDataRefetchInterval } = usePassportContext()
76
+
77
+ const { data, isError, isPending } = useGetCurrentAccount({
78
+ staleTime: accountDataRefetchInterval,
79
+ refetchInterval: accountDataRefetchInterval,
80
+ })
81
+ const [, theme] = useStyletron()
82
+
83
+ if (isError) {
84
+ return (
85
+ <AccountError
86
+ padding={theme.sizing.scale600}
87
+ paddingTop={theme.sizing.scale900}
88
+ paddingBottom={theme.sizing.scale800}
89
+ marginBottom={theme.sizing.scale300}
90
+ topic="account information"
91
+ />
92
+ )
93
+ }
94
+
95
+ if (isPending) {
96
+ return <WelcomeBlockSkeleton />
97
+ }
98
+
99
+ const [mezoId, mezoIdSuffix] = (data.mezoId ?? "").split(DOT_DELIMITER_REGEX)
100
+
101
+ return (
102
+ <Block
103
+ display="flex"
104
+ alignItems="center"
105
+ paddingLeft={theme.sizing.scale600}
106
+ paddingRight={theme.sizing.scale600}
107
+ paddingTop={theme.sizing.scale900}
108
+ paddingBottom={theme.sizing.scale800}
109
+ overrides={mergeOverrides(overrides, {
110
+ Block: {
111
+ style: {
112
+ gap: theme.sizing.scale600,
113
+ },
114
+ },
115
+ })}
116
+ {...restProps}
117
+ >
118
+ <Block
119
+ as={DefaultAvatar}
120
+ width={theme.sizing.scale1200}
121
+ height={theme.sizing.scale1200}
122
+ overrides={{
123
+ Block: {
124
+ style: {
125
+ borderRadius: "100%",
126
+ },
127
+ },
128
+ }}
129
+ />
130
+
131
+ <Block display="flex" flexDirection="column">
132
+ {mezoId && mezoIdSuffix && (
133
+ <HeadingMedium as="span">
134
+ <Block as="span">{mezoId}</Block>
135
+ <Block as="span" color={theme.colors.contentSecondary}>
136
+ {mezoIdSuffix}
137
+ </Block>
138
+ </HeadingMedium>
139
+ )}
140
+
141
+ <Block
142
+ display="flex"
143
+ alignItems="center"
144
+ overrides={{
145
+ Block: {
146
+ style: {
147
+ gap: theme.sizing.scale100,
148
+ },
149
+ },
150
+ }}
151
+ >
152
+ <Mats
153
+ size={theme.sizing.scale600}
154
+ overrides={{
155
+ Svg: {
156
+ style: {
157
+ margin: theme.sizing.scale100, // To reduce gap
158
+ },
159
+ },
160
+ }}
161
+ />
162
+
163
+ <LabelLarge color={theme.colors.contentPrimary}>
164
+ <SlotNumber formatFunction={formatNumberToCompactString}>
165
+ {data.totalMats}
166
+ </SlotNumber>{" "}
167
+ mats
168
+ </LabelLarge>
169
+ </Block>
170
+ </Block>
171
+ </Block>
172
+ )
173
+ }
@@ -0,0 +1,212 @@
1
+ import {
2
+ Block,
3
+ ButtonIcon,
4
+ Check,
5
+ Close,
6
+ Input,
7
+ LabelSmall,
8
+ ParagraphSmall,
9
+ Spinner,
10
+ useStyletron,
11
+ } from "@mezo-org/mezo-clay"
12
+ import React, { useCallback, useEffect, useState } from "react"
13
+ import EditIcon from "../../../assets/EditIcon"
14
+
15
+ type InlineEditFieldProps = {
16
+ label: string
17
+ value: string
18
+ placeholder?: string
19
+ onSave: (value: string) => Promise<void>
20
+ validate?: (value: string) => string | null
21
+ isLoading?: boolean
22
+ }
23
+
24
+ type InlineFieldWrapperProps = {
25
+ label: string
26
+ children: React.ReactNode
27
+ }
28
+
29
+ function InlineFieldWrapper({ label, children }: InlineFieldWrapperProps) {
30
+ const [, theme] = useStyletron()
31
+
32
+ return (
33
+ <Block
34
+ display="flex"
35
+ flexDirection="column"
36
+ overrides={{
37
+ Block: {
38
+ style: {
39
+ gap: theme.sizing.scale200,
40
+ },
41
+ },
42
+ }}
43
+ >
44
+ <LabelSmall color={theme.colors.contentSecondary}>{label}</LabelSmall>
45
+ {children}
46
+ </Block>
47
+ )
48
+ }
49
+
50
+ export default function InlineEditField(props: InlineEditFieldProps) {
51
+ const { label, value, placeholder, onSave, validate, isLoading } = props
52
+
53
+ const [, theme] = useStyletron()
54
+ const [isEditing, setIsEditing] = useState(false)
55
+ const [editValue, setEditValue] = useState(value)
56
+ const [error, setError] = useState<string | null>(null)
57
+ const [isSaving, setIsSaving] = useState(false)
58
+
59
+ useEffect(() => {
60
+ setEditValue(value)
61
+ }, [value])
62
+
63
+ const handleEdit = useCallback(() => {
64
+ setIsEditing(true)
65
+ setEditValue(value)
66
+ setError(null)
67
+ }, [value])
68
+
69
+ const handleCancel = useCallback(() => {
70
+ setIsEditing(false)
71
+ setEditValue(value)
72
+ setError(null)
73
+ }, [value])
74
+
75
+ const handleSave = useCallback(async () => {
76
+ if (validate) {
77
+ const validationError = validate(editValue)
78
+ if (validationError) {
79
+ setError(validationError)
80
+ return
81
+ }
82
+ }
83
+
84
+ setIsSaving(true)
85
+ try {
86
+ await onSave(editValue)
87
+ setIsEditing(false)
88
+ setError(null)
89
+ } catch (err) {
90
+ setError(err instanceof Error ? err.message : "Failed to save")
91
+ } finally {
92
+ setIsSaving(false)
93
+ }
94
+ }, [editValue, onSave, validate])
95
+
96
+ const handleKeyDown = useCallback(
97
+ (e: React.KeyboardEvent) => {
98
+ if (e.key === "Enter") {
99
+ handleSave()
100
+ } else if (e.key === "Escape") {
101
+ handleCancel()
102
+ }
103
+ },
104
+ [handleSave, handleCancel],
105
+ )
106
+
107
+ if (isLoading) {
108
+ return (
109
+ <InlineFieldWrapper label={label}>
110
+ <Block
111
+ display="flex"
112
+ alignItems="center"
113
+ height={theme.sizing.scale950}
114
+ >
115
+ <Spinner $size={theme.sizing.scale600} />
116
+ </Block>
117
+ </InlineFieldWrapper>
118
+ )
119
+ }
120
+
121
+ if (isEditing) {
122
+ return (
123
+ <InlineFieldWrapper label={label}>
124
+ <Block
125
+ display="flex"
126
+ alignItems="center"
127
+ width="100%"
128
+ gridGap="scale300"
129
+ >
130
+ <Block flex={1}>
131
+ <Input
132
+ value={editValue}
133
+ onChange={(e) => {
134
+ setEditValue(e.currentTarget.value)
135
+ setError(null)
136
+ }}
137
+ onKeyDown={handleKeyDown}
138
+ placeholder={placeholder}
139
+ size="small"
140
+ error={!!error}
141
+ disabled={isSaving}
142
+ autoFocus
143
+ />
144
+ </Block>
145
+ <ButtonIcon
146
+ onClick={handleSave}
147
+ size="small"
148
+ shape="circle"
149
+ kind="secondary"
150
+ disabled={isSaving}
151
+ aria-label="Save"
152
+ >
153
+ {isSaving ? (
154
+ <Spinner $size={theme.sizing.scale500} />
155
+ ) : (
156
+ <Check size={14} color="currentColor" />
157
+ )}
158
+ </ButtonIcon>
159
+ <ButtonIcon
160
+ onClick={handleCancel}
161
+ size="small"
162
+ shape="circle"
163
+ kind="tertiary"
164
+ disabled={isSaving}
165
+ aria-label="Cancel"
166
+ >
167
+ <Close size={14} color="currentColor" />
168
+ </ButtonIcon>
169
+ </Block>
170
+ {error && (
171
+ <ParagraphSmall
172
+ color={theme.colors.contentNegative}
173
+ marginTop={0}
174
+ marginBottom={0}
175
+ >
176
+ {error}
177
+ </ParagraphSmall>
178
+ )}
179
+ </InlineFieldWrapper>
180
+ )
181
+ }
182
+
183
+ return (
184
+ <InlineFieldWrapper label={label}>
185
+ <Block
186
+ display="flex"
187
+ alignItems="center"
188
+ justifyContent="space-between"
189
+ onClick={handleEdit}
190
+ overrides={{
191
+ Block: {
192
+ style: {
193
+ cursor: "pointer",
194
+ minHeight: theme.sizing.scale950,
195
+ },
196
+ },
197
+ }}
198
+ >
199
+ <ParagraphSmall
200
+ marginTop={0}
201
+ marginBottom={0}
202
+ color={
203
+ value ? theme.colors.contentPrimary : theme.colors.contentTertiary
204
+ }
205
+ >
206
+ {value || placeholder}
207
+ </ParagraphSmall>
208
+ <EditIcon size={16} color={theme.colors.contentTertiary} />
209
+ </Block>
210
+ </InlineFieldWrapper>
211
+ )
212
+ }
@@ -0,0 +1,87 @@
1
+ import { Block, ParagraphSmall, Link, useStyletron } from "@mezo-org/mezo-clay"
2
+ import React, { useCallback } from "react"
3
+ import NestedViewLayout from "../NestedViewLayout"
4
+ import InlineEditField from "./InlineEditField"
5
+ import { useGetCurrentAccount, useUpdateUserProfile } from "../../../hooks"
6
+ import { validateEmail, validateTelegram } from "../../../utils/validation"
7
+
8
+ function Settings() {
9
+ const [, theme] = useStyletron()
10
+
11
+ const { data: account, isPending: isCurrentAccountPending } =
12
+ useGetCurrentAccount()
13
+ const { updateProfileAsync } = useUpdateUserProfile()
14
+
15
+ const handleSaveEmail = useCallback(
16
+ async (value: string) => {
17
+ await updateProfileAsync({ email: value || "" })
18
+ },
19
+ [updateProfileAsync],
20
+ )
21
+
22
+ const handleSaveTelegram = useCallback(
23
+ async (value: string) => {
24
+ const handle = value.startsWith("@") ? value.slice(1) : value
25
+ await updateProfileAsync({ telegram: handle || "" })
26
+ },
27
+ [updateProfileAsync],
28
+ )
29
+
30
+ return (
31
+ <NestedViewLayout>
32
+ <ParagraphSmall
33
+ marginTop={0}
34
+ marginBottom={theme.sizing.scale800}
35
+ color={theme.colors.contentSecondary}
36
+ >
37
+ Update your contact information to stay connected.
38
+ </ParagraphSmall>
39
+
40
+ <Block
41
+ display="flex"
42
+ flexDirection="column"
43
+ overrides={{
44
+ Block: {
45
+ style: {
46
+ gap: theme.sizing.scale600,
47
+ },
48
+ },
49
+ }}
50
+ >
51
+ <InlineEditField
52
+ label="Email"
53
+ value={account?.email || ""}
54
+ placeholder="Enter your email"
55
+ onSave={handleSaveEmail}
56
+ validate={validateEmail}
57
+ isLoading={isCurrentAccountPending}
58
+ />
59
+
60
+ <InlineEditField
61
+ label="Telegram"
62
+ value={account?.telegram || ""}
63
+ placeholder="Enter your Telegram handle"
64
+ onSave={handleSaveTelegram}
65
+ validate={validateTelegram}
66
+ isLoading={isCurrentAccountPending}
67
+ />
68
+ </Block>
69
+
70
+ <ParagraphSmall
71
+ marginTop={theme.sizing.scale800}
72
+ marginBottom={0}
73
+ color={theme.colors.contentTertiary}
74
+ >
75
+ By providing your contact information, you agree to receive service
76
+ notifications and occasional promotional messages. Promotional messages
77
+ will always include an opt out link. The Privacy Policy is available{" "}
78
+ <Link href="https://mezo.org/privacy" target="_blank">
79
+ here
80
+ </Link>
81
+ .
82
+ </ParagraphSmall>
83
+ </NestedViewLayout>
84
+ )
85
+ }
86
+
87
+ export default Settings