Skip to content

Commit

Permalink
[Fix] Added email validation and added compact mode (#204)
Browse files Browse the repository at this point in the history
* Added email validation and added compact mode

* Changed login btn text

* Fixed a tag footer to open in new tab

* updated param name to set compact mode for future additions
  • Loading branch information
makylfang authored Aug 24, 2023
1 parent d8ba091 commit ecd5279
Show file tree
Hide file tree
Showing 12 changed files with 5,616 additions and 5,412 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@arcana/auth",
"version": "1.0.7",
"version": "1.0.8-beta.1",
"description": "Arcana Auth",
"main": "dist/standalone/auth.esm.js",
"type": "module",
Expand Down Expand Up @@ -61,6 +61,7 @@
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.3",
"@types/jest": "^27.4.1",
"@types/validator": "^13.11.1",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"babel-jest": "^29.0.2",
Expand Down Expand Up @@ -90,7 +91,8 @@
"dependencies": {
"@metamask/safe-event-emitter": "^2.0.0",
"eth-rpc-errors": "^4.0.3",
"penpal": "^6.0.1"
"penpal": "^6.0.1",
"validator": "^13.11.0"
},
"resolutions": {
"terser": "^5.15.0"
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
ThemeConfig,
UserInfo,
} from './typings'
import isEmail from 'validator/es/lib/isEmail'
import { getAppInfo, getImageUrls } from './appInfo'
import { ArcanaAuthError, ErrorNotInitialized } from './errors'
import { LOG_LEVEL, setExceptionReporter, setLogLevel } from './logger'
Expand Down Expand Up @@ -140,6 +141,7 @@ class AuthProvider {
loginList: logins,
mode: this.theme,
logo: this.logo.vertical,
options: this.params.connectOptions,
})
}
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -195,6 +197,10 @@ class AuthProvider {
if (await this.isLoggedIn()) {
return this._provider
}

if (!isEmail(email)) {
throw new Error('Invalid email')
}
await this._provider.initPasswordlessLogin(email)
if (emailSentHook) {
emailSentHook()
Expand Down Expand Up @@ -359,7 +365,7 @@ class AuthProvider {
if (this.connected) {
return resolve(this._provider)
}
this._provider.on('connect', () => {
this._provider.once('connect', () => {
return resolve(this._provider)
})
})
Expand Down
6 changes: 6 additions & 0 deletions src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export interface UserInfo {
picture?: string
address: string
publicKey: string
loginToken: string
}
export type Logins =
| 'google'
Expand Down Expand Up @@ -198,6 +199,10 @@ export interface ChainConfigInput {
chainId: string
}

export interface ConnectOptions {
compact: boolean
}

export interface ConstructorParams {
network: ('testnet' | 'dev' | 'mainnet') | NetworkConfig
debug: boolean
Expand All @@ -208,6 +213,7 @@ export interface ConstructorParams {
position: Position
setWindowProvider: boolean
appMode?: AppMode
connectOptions: ConnectOptions
}

type RequestArguments = {
Expand Down
68 changes: 42 additions & 26 deletions src/ui/components.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
import { ARCANA_LOGO, getSocialLogo } from './icons'
import { StateUpdater, useState } from 'preact/hooks'
import { StateUpdater, useEffect, useState } from 'preact/hooks'
import { ModalParams } from './typings'
import { Theme } from '../typings'
import { JSXInternal } from 'preact/src/jsx'
import isEmail from 'validator/es/lib/isEmail'
import ProgressOval from './loader'
import './style.css'

const Header = ({ mode, logo }: { mode: Theme; logo: string }) => {
const [renderLogoContainer, setRenderLogoContainer] = useState(true)
const removeLogoContainer = () => {
setRenderLogoContainer(false)
const Header = ({ compact, logo }: { compact: boolean; logo: string }) => {
const [loaded, setLoaded] = useState(false)
const showLogoContainer = () => {
setLoaded(false)
}
return (
<>
{renderLogoContainer && logo && (
<div className="xar-header-logo__container">
<img
className="xar-header-logo"
src={logo}
alt="app-logo"
onError={removeLogoContainer}
/>
{!loaded ? <div className="xar-header-logo__empty-container"></div> : ''}
<div
className="xar-header-logo__container"
style={loaded ? {} : { display: 'none' }}
>
<img
className="xar-header-logo"
src={logo}
alt="app-logo"
onLoad={showLogoContainer}
/>
</div>
{!compact ? (
<div className="xar-header-text">
<h1 className="xar-header-heading">Welcome</h1>
<p className="xar-header-subtext">
We’ll email you a login link for a password-free sign in.
</p>
</div>
) : (
''
)}
<div className="xar-header-text">
<h1 className="xar-header-heading">Welcome</h1>
<p className="xar-header-subtext">
We’ll email you a login link for a password-free sign in.
</p>
</div>
</>
)
}
Expand All @@ -41,8 +48,10 @@ const EmailLogin = ({
email: string
setEmail: StateUpdater<string>
} & Pick<ModalParams, 'loginWithLink'>) => {
const [disabled, setDisabled] = useState(true)
const onInput: JSXInternal.GenericEventHandler<HTMLInputElement> = (e) => {
setEmail(e.currentTarget.value)
setDisabled(!isEmail(e.currentTarget.value))
}

const clickHandler = async (
Expand All @@ -55,19 +64,21 @@ const EmailLogin = ({
await loginWithLink(email)
return
}

useEffect(() => {
setDisabled(!isEmail(email))
}, [])
return (
<form className="xar-email-login">
<label className="xar-email-login__label" htmlFor="">
Email
</label>
<input
value={email}
onInput={onInput}
className="xar-email-login__input"
type="text"
placeholder={'Enter your email'}
/>
<button onClick={clickHandler} className="xar-btn">
Get Link
<button disabled={disabled} onClick={clickHandler} className="xar-btn">
Get Login Link
</button>
</form>
)
Expand Down Expand Up @@ -109,7 +120,7 @@ const Footer = ({ mode }: { mode: Theme }) => {
const logo = ARCANA_LOGO[mode]
return (
<div className="xar-footer">
<a href="https://arcana.network" className="xar-footer-img__link">
<a href="https://arcana.network" target="_blank" className="xar-footer-img__link">
<img className="xar-footer-img" src={logo} alt="Secured By Arcana" />
</a>
</div>
Expand All @@ -120,14 +131,19 @@ const Loader = (props: {
text: string
children: preact.ComponentChildren
mode: Theme
compact: boolean
header?: JSXInternal.Element
}) => {
return (
<>
{props.header ? (
props.header
) : (
<ProgressOval stroke={8} secondaryColor="#8D8D8D" />
<ProgressOval
compact={props.compact}
stroke={8}
secondaryColor="#8D8D8D"
/>
)}
{props.text ? <p className="xar-loader__text">{props.text}</p> : ''}
{props.children ? <>{props.children}</> : ''}
Expand Down
6 changes: 4 additions & 2 deletions src/ui/loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ interface LoaderProps {
stroke: number
secondaryColor: string
strokeColor?: string
compact: boolean
}

export default function Loader(props: LoaderProps) {
const width = props.compact ? 60 : 80;
const { stroke = 8, secondaryColor } = props
return (
<div aria-label="oval-loading">
<svg
width={80}
height={80}
width={width}
height={width}
viewBox={getViewBoxSize(Number(stroke), RADIUS)}
xmlns="http://www.w3.org/2000/svg"
className="xar-loader-circle"
Expand Down
7 changes: 4 additions & 3 deletions src/ui/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const Modal = (props: ModalParams) => {
<Overlay>
<Container mode={props.mode}>
<Loader
compact={props.options.compact}
text={loaderState.text}
mode={props.mode}
header={
Expand All @@ -82,11 +83,11 @@ const Modal = (props: ModalParams) => {
) : undefined
}
>
{loaderState.type == 'LINK' || loaderState.type == 'LINK_SENT' ? (
{loaderState.type == 'LINK_SENT' ? (
<>
<Action
method={() => linkLogin()}
text="Send the email again"
text="Resend email"
/>
<Action
method={() => dispatch('RESET')}
Expand All @@ -103,7 +104,7 @@ const Modal = (props: ModalParams) => {
return (
<Overlay closeFunc={props.closeFunc}>
<Container mode={props.mode}>
<Header mode={props.mode} logo={props.logo} />
<Header compact={props.options.compact} logo={props.logo} />
<EmailLogin
email={email}
setEmail={setEmail}
Expand Down
3 changes: 3 additions & 0 deletions src/ui/modalController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ModalController {
mode: params.mode,
closeFunc: this.close,
logo: params.logo,
options: params.options,
}

this.createContainer()
Expand All @@ -37,9 +38,11 @@ class ModalController {
}

private createContainer() {
const modeClass = this.params.options.compact ?'compact': 'full'
this.container = document.createElement('div')
this.container.setAttribute('id', 'xar-login-container')
this.container.classList.add(`xar-${this.params.mode}-mode`)
this.container.classList.add(modeClass)
document.body.appendChild(this.container)
}
}
Expand Down
Loading

0 comments on commit ecd5279

Please sign in to comment.