@luxfi/core 4.4.10 → 4.4.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. package/components/access-code-input.tsx +71 -71
  2. package/components/auth/auth-listener.tsx +23 -41
  3. package/components/auth/auth-token/clear-auth-token.tsx +12 -0
  4. package/components/auth/auth-token/set-auth-token.tsx +16 -0
  5. package/components/auth/common-auth-domains.ts +16 -15
  6. package/components/auth/login-panel.tsx +119 -117
  7. package/components/chat-widget.tsx +77 -77
  8. package/components/commerce/bag-button.tsx +67 -67
  9. package/components/commerce/checkout-panel/close-button.tsx +26 -26
  10. package/components/commerce/checkout-panel/dt-bag-carousel.tsx +36 -36
  11. package/components/commerce/checkout-panel/dt-checkout-panel.tsx +66 -66
  12. package/components/commerce/checkout-panel/index.tsx +123 -123
  13. package/components/commerce/checkout-panel/links-row.tsx +21 -21
  14. package/components/commerce/checkout-panel/mb-checkout-panel.tsx +55 -55
  15. package/components/commerce/checkout-panel/steps-indicator.tsx +39 -39
  16. package/components/commerce/checkout-panel/thank-you.tsx +18 -18
  17. package/components/commerce/desktop-bag-popup.tsx +78 -78
  18. package/components/commerce/mobile-bag-drawer.tsx +51 -51
  19. package/components/commerce/mobile-menu-toggle-button.tsx +35 -35
  20. package/components/commerce/mobile-nav-menu.tsx +64 -64
  21. package/components/contact-dialog/contact-form.tsx +112 -112
  22. package/components/contact-dialog/disclaimer.tsx +13 -13
  23. package/components/contact-dialog/index.tsx +64 -64
  24. package/components/copyright.tsx +21 -21
  25. package/components/footer.tsx +77 -77
  26. package/components/header/desktop.tsx +54 -54
  27. package/components/header/index.tsx +26 -26
  28. package/components/header/mobile.tsx +161 -161
  29. package/components/header/theme-toggle.tsx +26 -26
  30. package/components/icons/bag-icon.tsx +10 -10
  31. package/components/icons/github.tsx +14 -14
  32. package/components/icons/index.tsx +35 -35
  33. package/components/icons/lux-logo.tsx +10 -10
  34. package/components/icons/secure-delivery.tsx +13 -13
  35. package/components/icons/social-icon.tsx +35 -35
  36. package/components/icons/social-svg.css +3 -3
  37. package/components/icons/youtube-logo.tsx +59 -59
  38. package/components/index.ts +27 -27
  39. package/components/logo.tsx +81 -81
  40. package/components/mini-chart/index.tsx +7 -7
  41. package/components/mini-chart/mini-chart-props.ts +43 -43
  42. package/components/mini-chart/mini-chart.tsx +85 -85
  43. package/components/mini-chart/wrapper.tsx +23 -23
  44. package/components/not-found/index.tsx +27 -27
  45. package/components/not-found/not-found-content.mdx +5 -5
  46. package/components/root-layout.tsx +71 -71
  47. package/components/scripts.tsx +23 -23
  48. package/conf/index.ts +50 -50
  49. package/environment.d.ts +5 -5
  50. package/next/analytics/fpixel.ts +15 -15
  51. package/next/analytics/google-analytics.ts +13 -13
  52. package/next/analytics/index.ts +3 -3
  53. package/next/analytics/pixel-analytics.tsx +54 -54
  54. package/next/determine-device-mw.ts +16 -16
  55. package/next/font/get-app-router-font-classes.ts +12 -12
  56. package/next/font/load-and-return-lux-next-fonts-on-import.ts +68 -68
  57. package/next/font/next-font-desc.ts +27 -27
  58. package/next/font/pages-router-font-vars.tsx +18 -18
  59. package/next/head-metadata/from-next/metadata-types.ts +158 -158
  60. package/next/head-metadata/from-next/opengraph-types.ts +267 -267
  61. package/next/head-metadata/from-next/twitter-types.ts +92 -92
  62. package/next/head-metadata/index.tsx +208 -208
  63. package/package.json +73 -73
  64. package/server-actions/firebase-app.ts +14 -14
  65. package/server-actions/index.ts +5 -5
  66. package/server-actions/store-contact.ts +51 -51
  67. package/site-def/footer/community.tsx +67 -67
  68. package/site-def/footer/company.ts +37 -37
  69. package/site-def/footer/ecosystem.ts +37 -37
  70. package/site-def/footer/index.tsx +26 -26
  71. package/site-def/footer/legal.ts +28 -28
  72. package/site-def/footer/network.ts +45 -45
  73. package/site-def/footer/svg/warpcast-logo.svg +11 -11
  74. package/site-def/index.ts +2 -2
  75. package/site-def/main-nav.ts +35 -35
  76. package/site-def/site-def.ts +36 -36
  77. package/style/lux-colors.css +85 -85
  78. package/style/lux-global.css +30 -30
  79. package/tailwind/fontFamily.tailwind.lux.ts +18 -18
  80. package/tailwind/index.ts +2 -2
  81. package/tailwind/lux-tw-fonts.ts +39 -39
  82. package/tailwind/tailwind.config.lux-preset.ts +10 -10
  83. package/tsconfig.json +10 -10
  84. package/types/contact-info.ts +10 -10
@@ -1,71 +1,71 @@
1
- 'use client'
2
-
3
- import { useState } from 'react'
4
- import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@hanzo/ui/primitives'
5
- import { cn } from '@hanzo/ui/util'
6
-
7
- const AccessCodeInput: React.FC<{
8
- onSuccess?: () => void
9
- onFail?: () => void
10
- validCodes?: string[]
11
- className?: string
12
- }> = ({
13
- onSuccess,
14
- onFail,
15
- validCodes,
16
- className
17
- }) => {
18
- const [status, setStatus] = useState<'valid' | 'invalid' | 'checking' | undefined>()
19
-
20
- const checkAccessCode = (code: string) => {
21
- setStatus(undefined)
22
- if (code.length === 6) {
23
- setStatus('checking')
24
- setTimeout(() => {
25
- if (validCodes?.includes(code) && onSuccess) {
26
- setStatus('valid')
27
- onSuccess()
28
- }
29
- else {
30
- setStatus('invalid')
31
- onFail && onFail()
32
- }
33
- }, 1000)
34
- }
35
- }
36
-
37
- return (
38
- <div className={cn('flex flex-col gap-2 mx-auto w-full text-center items-center', className)}>
39
- <InputOTP
40
- className='mx-auto'
41
- maxLength={6}
42
- onInput={(event) => checkAccessCode((event.target as HTMLInputElement).value)}
43
- render={({ slots }) => (
44
- <>
45
- <InputOTPGroup>
46
- {slots.slice(0, 3).map((slot, index) => (
47
- <InputOTPSlot key={index} {...slot} />
48
- ))}{" "}
49
- </InputOTPGroup>
50
- <InputOTPSeparator />
51
- <InputOTPGroup>
52
- {slots.slice(3).map((slot, index) => (
53
- <InputOTPSlot key={index + 3} {...slot} />
54
- ))}
55
- </InputOTPGroup>
56
- </>
57
- )}
58
- />
59
- <p className='h-[3rem]'>
60
- {
61
- status === 'checking' ? 'Checking access code...' :
62
- status === 'invalid' ? <span className='text-destructive'>Invalid access code!</span> :
63
- status === 'valid' ? <span>Access code is valid! Redirecting...</span> :
64
- null
65
- }
66
- </p>
67
- </div>
68
- )
69
- }
70
-
71
- export default AccessCodeInput
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@hanzo/ui/primitives'
5
+ import { cn } from '@hanzo/ui/util'
6
+
7
+ const AccessCodeInput: React.FC<{
8
+ onSuccess?: () => void
9
+ onFail?: () => void
10
+ validCodes?: string[]
11
+ className?: string
12
+ }> = ({
13
+ onSuccess,
14
+ onFail,
15
+ validCodes,
16
+ className
17
+ }) => {
18
+ const [status, setStatus] = useState<'valid' | 'invalid' | 'checking' | undefined>()
19
+
20
+ const checkAccessCode = (code: string) => {
21
+ setStatus(undefined)
22
+ if (code.length === 6) {
23
+ setStatus('checking')
24
+ setTimeout(() => {
25
+ if (validCodes?.includes(code) && onSuccess) {
26
+ setStatus('valid')
27
+ onSuccess()
28
+ }
29
+ else {
30
+ setStatus('invalid')
31
+ onFail && onFail()
32
+ }
33
+ }, 1000)
34
+ }
35
+ }
36
+
37
+ return (
38
+ <div className={cn('flex flex-col gap-2 mx-auto w-full text-center items-center', className)}>
39
+ <InputOTP
40
+ className='mx-auto'
41
+ maxLength={6}
42
+ onInput={(event) => checkAccessCode((event.target as HTMLInputElement).value)}
43
+ render={({ slots }) => (
44
+ <>
45
+ <InputOTPGroup>
46
+ {slots.slice(0, 3).map((slot, index) => (
47
+ <InputOTPSlot key={index} {...slot} />
48
+ ))}{" "}
49
+ </InputOTPGroup>
50
+ <InputOTPSeparator />
51
+ <InputOTPGroup>
52
+ {slots.slice(3).map((slot, index) => (
53
+ <InputOTPSlot key={index + 3} {...slot} />
54
+ ))}
55
+ </InputOTPGroup>
56
+ </>
57
+ )}
58
+ />
59
+ <p className='h-[3rem]'>
60
+ {
61
+ status === 'checking' ? 'Checking access code...' :
62
+ status === 'invalid' ? <span className='text-destructive'>Invalid access code!</span> :
63
+ status === 'valid' ? <span>Access code is valid! Redirecting...</span> :
64
+ null
65
+ }
66
+ </p>
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default AccessCodeInput
@@ -1,41 +1,23 @@
1
- 'use client'
2
-
3
- import { useEffect } from 'react'
4
- import { useAuth } from '@hanzo/auth/service'
5
-
6
- const AuthListener = () => {
7
- const auth = useAuth()
8
-
9
- const requestAuthToken = () => {
10
- const childFrame = document.getElementById('login') as HTMLIFrameElement
11
- childFrame?.contentWindow?.postMessage(true, process.env.NEXT_PUBLIC_LOGIN_SITE_URL ?? '')
12
- }
13
-
14
- useEffect(() => {
15
- const handleMessage = (event: any) => {
16
- if (event.origin === process.env.NEXT_PUBLIC_LOGIN_SITE_URL) {
17
- const token = event.data
18
- auth.loginWithCustomToken(token)
19
- }
20
- }
21
-
22
- if (typeof window !== 'undefined') {
23
- window.addEventListener('message', handleMessage)
24
-
25
- return () => {
26
- window.removeEventListener('message', handleMessage)
27
- }
28
- }
29
- }, [])
30
-
31
- return (
32
- <iframe
33
- id='login'
34
- onLoad={requestAuthToken}
35
- src={`${process.env.NEXT_PUBLIC_LOGIN_SITE_URL}/login`}
36
- className='hidden'
37
- />
38
- )
39
- }
40
-
41
- export default AuthListener
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { useAuth } from '@hanzo/auth/service'
5
+ import { getCookie } from 'cookies-next'
6
+
7
+ const AuthListener = () => {
8
+ const auth = useAuth()
9
+
10
+ useEffect(() => {
11
+ const authToken = getCookie('auth-token')
12
+ if (!!authToken) {
13
+ auth.loginWithCustomToken(authToken)
14
+ }
15
+ else {
16
+ auth.logout()
17
+ }
18
+ }, [])
19
+
20
+ return ( <></> )
21
+ }
22
+
23
+ export default AuthListener
@@ -0,0 +1,12 @@
1
+ import domains from '../common-auth-domains'
2
+
3
+ const ClearAuthToken = () => {
4
+ return (<>
5
+ {domains.map(({url}) => (
6
+ /* Clear auth-token cookie across all Lux domains */
7
+ <img src={`${url}/api/auth/clear-auth-token`} className='absolute hidden'/>
8
+ ))}
9
+ </>)
10
+ }
11
+
12
+ export default ClearAuthToken
@@ -0,0 +1,16 @@
1
+ import domains from '../common-auth-domains'
2
+
3
+ const SetAuthToken: React.FC<{
4
+ authToken: string
5
+ }> = ({
6
+ authToken,
7
+ }) => {
8
+ return (<>
9
+ {!!authToken && domains.map(({url}) => (
10
+ /* Set auth-token cookie across all Lux domains */
11
+ <img src={`${url}/api/auth/set-auth-token?token=${authToken}`} className='absolute hidden'/>
12
+ ))}
13
+ </>)
14
+ }
15
+
16
+ export default SetAuthToken
@@ -1,15 +1,16 @@
1
- const domains = [
2
- { id: 'lux.market', url: 'https://lux.market' },
3
- { id: 'lux.shop', url: 'https://lux.shop' },
4
- { id: 'lux.credit', url: 'https://lux.credit' },
5
- { id: 'lux.network', url: 'https://lux.network' },
6
- { id: 'wallet.lux.network', url: 'https://wallet.lux.network' },
7
- { id: 'safe.lux.network', url: 'https://safe.lux.network' },
8
- { id: 'lux.finance', url: 'https://lux.finance' },
9
- { id: 'lux.exchange', url: 'https://lux.exchange' },
10
- { id: 'lux.quest', url: 'https://lux.quest' },
11
- ]
12
-
13
- export {
14
- domains as default
15
- }
1
+ const domains = [
2
+ { id: 'lux.market', url: 'https://lux.market' },
3
+ { id: 'lux.shop', url: 'https://lux.shop' },
4
+ { id: 'lux.credit', url: 'https://lux.credit' },
5
+ { id: 'lux.network', url: 'https://lux.network' },
6
+ { id: 'wallet.lux.network', url: 'https://wallet.lux.network' },
7
+ { id: 'safe.lux.network', url: 'https://safe.lux.network' },
8
+ { id: 'lux.finance', url: 'https://lux.finance' },
9
+ { id: 'lux.exchange', url: 'https://lux.exchange' },
10
+ { id: 'lux.quest', url: 'https://lux.quest' },
11
+ { id: 'lux.services', url: 'https://lux.services' },
12
+ ]
13
+
14
+ export {
15
+ domains as default
16
+ }
@@ -1,117 +1,119 @@
1
- 'use client'
2
-
3
- import { useEffect } from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import Link from 'next/link'
6
- import Autoplay from 'embla-carousel-autoplay'
7
- import { getCookie , setCookie } from 'cookies-next'
8
-
9
- import { cn } from '@hanzo/ui/util'
10
- import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
11
- import { LoginPanel as Login } from '@hanzo/auth/components'
12
-
13
- import { Logo } from '..'
14
- import LuxLogo from '../icons/lux-logo'
15
- import { legal } from '../../site-def/footer'
16
- import domains from './common-auth-domains'
17
-
18
- const LoginPanel: React.FC<{
19
- close: () => void
20
- getStartedUrl?: string
21
- redirectUrl?: string
22
- className?: string
23
- reviews: { text: string, author: string, href: string }[]
24
- }> = ({
25
- close,
26
- getStartedUrl='/',
27
- redirectUrl,
28
- className='',
29
- reviews
30
- }) => {
31
- const router = useRouter()
32
-
33
- const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
34
- const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
35
-
36
- const onLogin = (token: string) => {
37
- setCookie('auth-token', token, { sameSite: 'none', secure: true })
38
- for (const { url } of domains) {
39
- parent?.contentWindow?.postMessage(token, url)
40
- }
41
- redirectUrl && router.replace(redirectUrl)
42
- }
43
-
44
- useEffect(() => {
45
- const handleMessage = (event: any) => {
46
- if (domains.includes(event.origin)) {
47
- const token = getCookie('auth-token')
48
- parent?.contentWindow?.postMessage(token, event.origin)
49
- }
50
- }
51
-
52
- if (typeof window !== 'undefined') {
53
- window.addEventListener('message', handleMessage)
54
-
55
- return () => {
56
- window.removeEventListener('message', handleMessage)
57
- }
58
- }
59
- }, [])
60
-
61
- return (
62
- <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
63
- <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
64
- <div className='h-full w-full max-w-[750px] px-8 pt-0'>
65
- <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
66
- <Button
67
- variant='ghost'
68
- onClick={close}
69
- className='w-fit !min-w-0 p-2'
70
- >
71
- <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
72
- </Button>
73
- <Carousel
74
- options={{ align: 'center', loop: true }}
75
- className='w-full'
76
- plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
77
- >
78
- <CarouselContent>
79
- {reviews.map(({text, author, href}, index) => (
80
- <CarouselItem key={index}>
81
- <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
82
- <p>“{text}“</p>
83
- <p className='text-sm'>{author}</p>
84
- </Link>
85
- </CarouselItem>
86
- ))}
87
- </CarouselContent>
88
- </Carousel>
89
- </div>
90
- </div>
91
- </div>
92
- <div className='w-full h-full bg-background flex flex-row items-center'>
93
- <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
94
- <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
95
- <Button
96
- variant='ghost'
97
- onClick={close}
98
- className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
99
- >
100
- <LuxLogo className='w-5 h-5'/>
101
- </Button>
102
- <Login
103
- getStartedUrl={getStartedUrl}
104
- redirectUrl={redirectUrl}
105
- className='w-full max-w-sm'
106
- termsOfServiceUrl={termsOfServiceUrl}
107
- privacyPolicyUrl={privacyPolicyUrl}
108
- onLoginChanged={onLogin}
109
- />
110
- </div>
111
- </div>
112
- </div>
113
- </div>
114
- )
115
- }
116
-
117
- export default LoginPanel
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import Link from 'next/link'
6
+ import Autoplay from 'embla-carousel-autoplay'
7
+ import { getCookie } from 'cookies-next'
8
+
9
+ import { cn } from '@hanzo/ui/util'
10
+ import { Button, Carousel, CarouselContent, CarouselItem } from '@hanzo/ui/primitives'
11
+ import { LoginPanel as Login } from '@hanzo/auth/components'
12
+
13
+ import { Logo } from '..'
14
+ import LuxLogo from '../icons/lux-logo'
15
+ import { legal } from '../../site-def/footer'
16
+ import domains from './common-auth-domains'
17
+ import SetAuthToken from './auth-token/set-auth-token'
18
+ import ClearAuthToken from './auth-token/clear-auth-token'
19
+
20
+ const LoginPanel: React.FC<{
21
+ close: () => void
22
+ getStartedUrl?: string
23
+ redirectUrl?: string
24
+ className?: string
25
+ reviews: { text: string, author: string, href: string }[]
26
+ }> = ({
27
+ close,
28
+ getStartedUrl='/',
29
+ redirectUrl,
30
+ className='',
31
+ reviews
32
+ }) => {
33
+ const router = useRouter()
34
+
35
+ const [authToken, setAuthToken] = useState<string>(getCookie('auth-token') ?? '')
36
+
37
+ const termsOfServiceUrl = legal.find(({title}) => title === 'Terms and Conditions')?.href || ''
38
+ const privacyPolicyUrl = legal.find(({title}) => title === 'Privacy Policy')?.href || ''
39
+
40
+ const onLogin = (token: string) => {
41
+ setAuthToken(token)
42
+
43
+ const promises = domains.map(({url}) => {
44
+ return new Promise<void>((resolve, reject) => {
45
+ const img = new Image()
46
+ img.onload = () => {
47
+ document.body.appendChild(img)
48
+ resolve()
49
+ }
50
+ img.onerror = reject
51
+ img.src = url
52
+ })
53
+ })
54
+
55
+ Promise.all(promises)
56
+ .then(() => redirectUrl && router.replace(redirectUrl))
57
+ .catch(error => console.error('An image failed to load', error));
58
+ }
59
+
60
+ return (<>
61
+ {!!authToken && <SetAuthToken authToken={authToken} />}
62
+ {!authToken && <ClearAuthToken />}
63
+
64
+ <div className={cn('grid grid-cols-1 md:grid-cols-2', className)}>
65
+ <div className='hidden md:flex w-full h-full bg-level-1 flex-row items-end justify-end overflow-y-auto min-h-screen'>
66
+ <div className='h-full w-full max-w-[750px] px-8 pt-0'>
67
+ <div className='h-full w-full max-w-[550px] mx-auto flex flex-col justify-between min-h-screen py-10'>
68
+ <Button
69
+ variant='ghost'
70
+ onClick={close}
71
+ className='w-fit !min-w-0 p-2'
72
+ >
73
+ <Logo size='md' spanClassName='!cursor-pointer' layout='text-only'/>
74
+ </Button>
75
+ <Carousel
76
+ options={{ align: 'center', loop: true }}
77
+ className='w-full'
78
+ plugins={[Autoplay({ delay: 5000, stopOnInteraction: true })]}
79
+ >
80
+ <CarouselContent>
81
+ {reviews.map(({text, author, href}, index) => (
82
+ <CarouselItem key={index}>
83
+ <Link href={href} className='flex flex-col gap-3 cursor-pointer'>
84
+ <p>“{text}“</p>
85
+ <p className='text-sm'>{author}</p>
86
+ </Link>
87
+ </CarouselItem>
88
+ ))}
89
+ </CarouselContent>
90
+ </Carousel>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ <div className='w-full h-full bg-background flex flex-row items-center'>
95
+ <div className='w-full max-w-[750px] relative flex flex-col items-center px-8 pt-0 text-center'>
96
+ <div className='relative h-full w-full max-w-[400px] mx-auto flex flex-col gap-4 items-center py-10'>
97
+ <Button
98
+ variant='ghost'
99
+ onClick={close}
100
+ className='block md:hidden absolute rounded-full p-2 left-0 h-auto hover:bg-background'
101
+ >
102
+ <LuxLogo className='w-5 h-5'/>
103
+ </Button>
104
+ <Login
105
+ getStartedUrl={getStartedUrl}
106
+ redirectUrl={redirectUrl}
107
+ className='w-full max-w-sm'
108
+ termsOfServiceUrl={termsOfServiceUrl}
109
+ privacyPolicyUrl={privacyPolicyUrl}
110
+ onLoginChanged={onLogin}
111
+ />
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </>)
117
+ }
118
+
119
+ export default LoginPanel