diff --git a/examples/index.html b/examples/index.html index b9a672f..86e2b14 100644 --- a/examples/index.html +++ b/examples/index.html @@ -78,5 +78,9 @@

STATUS

+ diff --git a/src/appInfo.ts b/src/appInfo.ts index 998eca9..f37af85 100644 --- a/src/appInfo.ts +++ b/src/appInfo.ts @@ -1,4 +1,4 @@ -import { AppThemeInfo, AppInfo, Theme } from './typings' +import { AppInfo, AppThemeInfo, Theme } from './typings' function getImageUrls(appId: string, theme: Theme, gatewayUrl: string) { const u = new URL(`/api/v2/app/${appId}/logo/?type=${theme}`, gatewayUrl) @@ -20,4 +20,4 @@ async function getAppInfo(appId: string, gatewayUrl: string) { return appInfo } -export { getImageUrls, getAppInfo, getAppThemeInfo } +export { getAppInfo, getAppThemeInfo, getImageUrls } diff --git a/src/index.ts b/src/index.ts index 44611fc..9ca448c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,26 +1,21 @@ -import { ArcanaProvider } from './provider' -import IframeWrapper from './iframeWrapper' -import { - getConstructorParams, - getErrorReporter, - getParamsFromClientId, - isClientId, - onWindowReady, - preLoadIframe, - removeHexPrefix, - validateAppAddress, -} from './utils' +import { getAppInfo, getAppThemeInfo, getImageUrls } from './appInfo' import { getNetworkConfig } from './config' +import { ArcanaAuthError, ErrorNotInitialized } from './errors' +import IframeWrapper from './iframeWrapper' +import { LOG_LEVEL, setExceptionReporter, setLogLevel } from './logger' +import Popup from './popup' +import { ArcanaProvider } from './provider' +import { ArcanaSolanaAPI } from './solana' import { AppConfig, AppMode, BearerAuthentication, + BearerAuthParams, ChainType, ConstructorParams, CustomProviderParams, EIP6963ProviderInfo, EthereumProvider, - BearerAuthParams, InitStatus, Logins, NetworkConfig, @@ -29,12 +24,17 @@ import { ThemeConfig, UserInfo, } from './typings' -import { getAppInfo, getAppThemeInfo, getImageUrls } from './appInfo' -import { ArcanaAuthError, ErrorNotInitialized } from './errors' -import { LOG_LEVEL, setExceptionReporter, setLogLevel } from './logger' -import Popup from './popup' import { ModalController } from './ui/modalController' -import { ArcanaSolanaAPI } from './solana' +import { + getConstructorParams, + getErrorReporter, + getParamsFromClientId, + isClientId, + onWindowReady, + preLoadIframe, + removeHexPrefix, + validateAppAddress, +} from './utils' import isEmail from 'validator/es/lib/isEmail' @@ -192,6 +192,7 @@ class AuthProvider { mode: this.theme, logo: this.logo.vertical, options: this.params.connectOptions, + theme_settings: this.appConfig.theme_settings, }) } return new Promise((resolve, reject) => { @@ -462,9 +463,10 @@ class AuthProvider { getAppThemeInfo(this.appId, this.networkConfig.gatewayUrl), getAppInfo(this.appId, this.networkConfig.gatewayUrl), ]) + const appImageURLs = getImageUrls( this.appId, - this.params.theme, + appThemeInfo.theme, this.networkConfig.gatewayUrl ) const horizontalLogo = @@ -484,7 +486,14 @@ class AuthProvider { vertical: verticalLogo ? appImageURLs.vertical : '', }, }, - theme: this.params.theme, + theme: appThemeInfo.theme, + }, + theme_settings: { + accent_color: appInfo.theme_settings.accent_color, + font_pairing: appInfo.theme_settings.font_pairing, + font_color: appInfo.theme_settings.font_color, + radius: appInfo.theme_settings.radius, + font_size: appInfo.theme_settings.font_size, }, } } @@ -595,15 +604,15 @@ class AuthProvider { } export { + AppConfig, AuthProvider, + BearerAuthentication, ConstructorParams, EthereumProvider, - BearerAuthentication, - AppConfig, - Theme, - Position, Logins, - UserInfo, - ThemeConfig, NetworkConfig, + Position, + Theme, + ThemeConfig, + UserInfo, } diff --git a/src/typings.ts b/src/typings.ts index 861b73b..d32d4db 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -43,6 +43,11 @@ export interface JsonRpcSuccess extends JsonRpcResponseBase { result: Maybe } +export type FirebaseBearer = { + uid?: string | undefined + token: string +} + export type JsonRpcResponse = JsonRpcSuccess | JsonRpcFailure /* end of json-rpc-engine types */ @@ -84,15 +89,25 @@ export interface AppThemeInfo { light_vertical?: string } } + +export interface ThemeSettings { + accent_color: string + font_pairing: string + font_size: string + font_color: string + radius: string +} export interface AppInfo { name: string chain_type: 'evm' | 'solana' + theme_settings: ThemeSettings } export interface AppConfig { name: string chainType: ChainType themeConfig: ThemeConfig + theme_settings: ThemeSettings } export interface UserInfo { diff --git a/src/ui/components.tsx b/src/ui/components.tsx index a185de6..ab03703 100644 --- a/src/ui/components.tsx +++ b/src/ui/components.tsx @@ -1,23 +1,33 @@ import { + Dispatch, StateUpdater, useEffect, - useState, useRef, - Dispatch, + useState, } from 'preact/hooks' -import { ARCANA_LOGO, getSocialLogo, MISC_ICONS } from './icons' -import { ModalParams } from './typings' -import { Theme } from '../typings' import { JSXInternal } from 'preact/src/jsx' import isEmail from 'validator/es/lib/isEmail' +import { Theme, ThemeSettings } from '../typings' +import { getFontFaimly, getFontSizeStyle, getRadius } from '../utilsFunction' +import { ARCANA_LOGO, getSocialLogo, MISC_ICONS } from './icons' import ProgressOval from './loader' import './style.css' +import { ModalParams } from './typings' -const Header = ({ compact, logo }: { compact: boolean; logo: string }) => { +const Header = ({ + compact, + logo, + theme_settings, +}: { + compact: boolean + logo: string + theme_settings: ThemeSettings +}) => { const [loaded, setLoaded] = useState(false) const showLogoContainer = () => { setLoaded(true) } + return ( <> {!loaded && logo ? ( @@ -38,7 +48,16 @@ const Header = ({ compact, logo }: { compact: boolean; logo: string }) => { {!compact ? (
-

Log in or sign up

+

+ Log In +

) : ( '' @@ -52,11 +71,12 @@ const EmailLogin = ({ email, setEmail, mode, + theme_settings, }: { email: string setEmail: Dispatch> mode: Theme -} & Pick) => { +} & Pick) => { const [disabled, setDisabled] = useState(true) const onInput: JSXInternal.GenericEventHandler = (e) => { setEmail(e.currentTarget.value) @@ -90,11 +110,23 @@ const EmailLogin = ({ className="xar-email-login__input" type="text" placeholder={'Enter your email'} + style={{ + fontFamily: getFontFaimly(theme_settings.font_pairing) + .primaryFontClass, + color: theme_settings.font_color, + font: getFontSizeStyle(Number(theme_settings.font_size)), + }} /> @@ -103,10 +135,25 @@ const EmailLogin = ({ ) } -const Separator = ({ text }: { text: string }) => { +const Separator = ({ + text, + theme_settings, +}: { + text: string + theme_settings: ThemeSettings +}) => { return (
- {text} + + {text} +
) } @@ -116,9 +163,10 @@ const SocialLogin = ({ loginList, mode, setShowMore, + theme_settings, }: { setShowMore: Dispatch> } & Pick< ModalParams, - 'loginWithSocial' | 'loginList' + 'loginWithSocial' | 'loginList' | 'theme_settings' > & { mode: Theme }) => { const clickHandler = (p: string) => { return loginWithSocial(p) @@ -131,13 +179,29 @@ const SocialLogin = ({
clickHandler(l)} + style={{ + backgroundColor: theme_settings.accent_color, + fontFamily: getFontFaimly(theme_settings.font_pairing) + .primaryFontClass, + color: theme_settings.font_color, + font: getFontSizeStyle(Number(theme_settings.font_size)), + }} > {`${l} -

Continue with {l.charAt(0).toUpperCase() + l.slice(1)}

+

+ Continue with {l.charAt(0).toUpperCase() + l.slice(1)} +

) }) @@ -146,13 +210,30 @@ const SocialLogin = ({
clickHandler(l)} + style={{ + backgroundColor: theme_settings.accent_color, + fontFamily: getFontFaimly(theme_settings.font_pairing) + .primaryFontClass, + color: theme_settings.font_color, + font: getFontSizeStyle(Number(theme_settings.font_size)), + }} > {`${l} -

Continue with {l.charAt(0).toUpperCase() + l.slice(1)}

+

+ Continue with {l.charAt(0).toUpperCase() + l.slice(1)} +

) : (
{ +const Footer = ({ + mode, + theme_settings, +}: { + mode: Theme + theme_settings: ThemeSettings +}) => { const logo = ARCANA_LOGO[mode] return (
@@ -211,11 +298,26 @@ const Loader = (props: { mode: Theme compact: boolean header?: JSXInternal.Element + theme_settings: ThemeSettings }) => { return ( <> {props.header ? props.header : } - {props.text ?

{props.text}

: ''} + {props.text ? ( +

+ {props.text} +

+ ) : ( + '' + )} {props.children ? <>{props.children} : ''} ) @@ -230,6 +332,7 @@ const OTPEntry = ({ email, mode, toHome, + theme_settings, }: { loginWithOtpStart: () => Promise<{ begin: () => Promise @@ -245,6 +348,7 @@ const OTPEntry = ({ email: string mode: Theme toHome(): void + theme_settings: ThemeSettings }) => { const { counter, resetCounter } = useCounter(30) const [attempts, setAttempts] = useState(3) @@ -435,7 +539,17 @@ const OTPEntry = ({ return ( <> -
{loader.text}
+
+ {loader.text} +
) } @@ -445,20 +559,54 @@ const OTPEntry = ({
-

Enter OTP

+

+ Enter OTP +

email
-
+
We’ve sent a verification code to
- {email} + + {email} +
@@ -482,13 +630,27 @@ const OTPEntry = ({ ? 'xar-otp-input xar-invalid-otp' : 'xar-otp-input' } + style={{ + fontFamily: getFontFaimly(theme_settings.font_pairing) + .primaryFontClass, + color: theme_settings.font_color, + font: getFontSizeStyle(Number(theme_settings.font_size)), + }} /> ) })}
{isInvalidOTP ? (
-

+

Incorrect OTP. {attempts} attempts left.

@@ -499,11 +661,35 @@ const OTPEntry = ({
{counter > 0 ? ( - Resend code in {counter} seconds + + Resend code in {counter} seconds + ) : (
- Did not receive your code yet? - + + Did not receive your code yet? + +
)}
@@ -532,17 +718,53 @@ const useCounter = (time = 60) => { return { counter, resetCounter } } -const OTPError = ({ action, mode }: { action: () => void; mode: Theme }) => { +const OTPError = ({ + action, + mode, + theme_settings, +}: { + action: () => void + mode: Theme + theme_settings: ThemeSettings +}) => { return ( <> -

Login Failed

-

+

+ Login Failed +

+

Please check credentials and try again

) @@ -551,14 +773,25 @@ const OTPError = ({ action, mode }: { action: () => void; mode: Theme }) => { const Container = ({ children, mode, + theme_settings, }: { mode: Theme children: preact.ComponentChildren + theme_settings: ThemeSettings }) => { return ( -
+
{children}
-
+
) } @@ -567,10 +800,12 @@ const Action = ({ text, method, disabled = false, + theme_settings, }: { text: string method: () => void disabled?: boolean + theme_settings: ThemeSettings }) => { return (
@@ -578,6 +813,12 @@ const Action = ({ disabled={disabled} onClick={() => method()} className="xar-action__link" + style={{ + fontFamily: getFontFaimly(theme_settings.font_pairing) + .primaryFontClass, + color: theme_settings.font_color, + font: getFontSizeStyle(Number(theme_settings.font_size)), + }} > {text} @@ -587,13 +828,13 @@ const Action = ({ export { Action, - Header, + Container, EmailLogin, - Separator, - SocialLogin, Footer, + Header, Loader, - Container, OTPEntry, OTPError, + Separator, + SocialLogin, } diff --git a/src/ui/modal.tsx b/src/ui/modal.tsx index 61e4c7a..7b57774 100644 --- a/src/ui/modal.tsx +++ b/src/ui/modal.tsx @@ -83,7 +83,7 @@ const Modal = (props: ModalParams) => { if (loaderState.loading) { return ( - + {loaderState.type == 'OTP_SENT' ? ( dispatch('RESET')} @@ -94,12 +94,14 @@ const Modal = (props: ModalParams) => { compact={props.options.compact} email={email} mode={props.mode} + theme_settings={props.theme_settings} /> ) : ( )} @@ -109,26 +111,36 @@ const Modal = (props: ModalParams) => { return ( - + {loaderState.type == 'OTP_ERROR' ? ( - dispatch('RESET')} mode={props.mode} /> + dispatch('RESET')} + mode={props.mode} + theme_settings={props.theme_settings} + /> ) : ( <> -
+
{props.loginList.length > 0 ? ( <> - + onShowMore(true)} + theme_settings={props.theme_settings} /> ) : null} @@ -138,6 +150,7 @@ const Modal = (props: ModalParams) => { setShow={() => onShowMore(false)} mode={props.mode} onLoginClick={socialLogin} + theme_settings={props.theme_settings} /> ) : ( '' diff --git a/src/ui/modalController.tsx b/src/ui/modalController.tsx index fee8980..580971f 100644 --- a/src/ui/modalController.tsx +++ b/src/ui/modalController.tsx @@ -17,6 +17,7 @@ class ModalController { closeFunc: this.close, logo: params.logo, options: params.options, + theme_settings: params.theme_settings, } this.createContainer() diff --git a/src/ui/more.tsx b/src/ui/more.tsx index e20cf35..9314070 100644 --- a/src/ui/more.tsx +++ b/src/ui/more.tsx @@ -1,12 +1,14 @@ import { StateUpdater, Dispatch } from 'preact/hooks' import { getSocialLogo, MISC_ICONS } from './icons' -import { Theme } from '../typings' +import { Theme, ThemeSettings } from '../typings' +import { getFontFaimly, getFontSizeStyle } from '../utilsFunction' interface MoreProps { list: Array setShow: Dispatch> onLoginClick: (kind: string) => void mode: Theme + theme_settings: ThemeSettings } export default function More(props: MoreProps) { @@ -29,6 +31,17 @@ export default function More(props: MoreProps) { onClick={() => setShow(false)} />
+

+ Continue with a social account +

{list.map((l) => { return ( diff --git a/src/ui/style.css b/src/ui/style.css index 586ba75..5f41912 100644 --- a/src/ui/style.css +++ b/src/ui/style.css @@ -27,6 +27,45 @@ } } +/* styles.css */ +@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&family=Onest:wght@100..900&family=PT+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Syne:wght@400..800&display=swap'); + +.pt-sans { + font-family: 'PT Sans', sans-serif; + font-style: normal; +} + +.nunito { + font-family: 'Nunito Sans', sans-serif; + font-style: normal; +} + +.onest { + font-family: Onest, sans-serif; + font-style: normal; +} + +.syne { + font-family: Syne, sans-serif; + font-style: normal; +} + +.inter { + font-family: Inter, sans-serif; + font-style: normal; +} + +.nohemi { + font-family: Nohemi, sans-serif; + font-style: normal; +} + +/* Scrollbar styles */ +::-webkit-scrollbar { + width: 4px; + height: 4px; +} + .xar-light-mode { --fg: #e4e9eb; --bg: #e4e9eb; diff --git a/src/ui/typings.ts b/src/ui/typings.ts index d72c1dd..1f5137f 100644 --- a/src/ui/typings.ts +++ b/src/ui/typings.ts @@ -1,4 +1,4 @@ -import { ConnectOptions, Theme } from '../typings' +import { ConnectOptions, Theme, ThemeSettings } from '../typings' type ModalParams = { loginWithOTPStart: ( @@ -11,6 +11,7 @@ type ModalParams = { mode: Theme logo: string options: ConnectOptions + theme_settings: ThemeSettings } export { ModalParams } diff --git a/src/utilsFunction.ts b/src/utilsFunction.ts new file mode 100644 index 0000000..541e64e --- /dev/null +++ b/src/utilsFunction.ts @@ -0,0 +1,40 @@ +const getFontSizeStyle = (font: number) => { + switch (font) { + case 1: + return '0.75rem' + case 2: + return '0.875rem' + case 3: + return '1rem' + default: + return '0.75rem' + } +} + +const getFontFaimly = (font_pairing: string) => { + if (!font_pairing) { + return { primaryFontClass: 'nohemi', secondaryFontClass: 'inter' } + } + + const [primaryFont, secondaryFont] = font_pairing.split(' + ') + const primaryFontClass = primaryFont?.toLowerCase() + const secondaryFontClass = secondaryFont?.toLowerCase() + return { primaryFontClass, secondaryFontClass } +} + +const getRadius = (radius: string) => { + switch (radius) { + case 'S': + return '4px' + case 'M': + return '8px' + case 'L': + return '12px' + case 'XL': + return '16px' + default: + return '0px' + } +} + +export { getFontSizeStyle, getFontFaimly, getRadius }