| 1 |
import { Spinner, Button } from '@wordpress/components' |
| 2 |
import { safeHTML } from '@wordpress/dom' |
| 3 |
import { useState, useEffect, useRef } from '@wordpress/element' |
| 4 |
import { __, sprintf } from '@wordpress/i18n' |
| 5 |
import { Icon } from '@wordpress/icons' |
| 6 |
import classNames from 'classnames' |
| 7 |
import { User as UserApi } from '@library/api/User' |
| 8 |
import { useIsDevMode } from '@library/hooks/helpers' |
| 9 |
import { useUserStore } from '@library/state/User' |
| 10 |
import { user } from '../../icons' |
| 11 |
import { success as successIcon } from '../../icons' |
| 12 |
|
| 13 |
export default function LoginInterface({ actionCallback, initialFocus }) { |
| 14 |
const loggedIn = useUserStore((state) => state.apiKey.length) |
| 15 |
const [email, setEmail] = useState('') |
| 16 |
const [apiKey, setApiKey] = useState('') |
| 17 |
const [feedback, setFeedback] = useState('') |
| 18 |
const [feedbackType, setFeedbackType] = useState('info') |
| 19 |
const [isWorking, setIsWorking] = useState(false) |
| 20 |
const [success, setSuccess] = useState(false) |
| 21 |
const viewPatternsButtonRef = useRef(null) |
| 22 |
const licenseKeyRef = useRef(null) |
| 23 |
const devMode = useIsDevMode() |
| 24 |
|
| 25 |
useEffect(() => { |
| 26 |
setEmail(useUserStore.getState().email) |
| 27 |
// This will reset the default error state to info |
| 28 |
return () => setFeedbackType('info') |
| 29 |
}, []) |
| 30 |
|
| 31 |
useEffect(() => { |
| 32 |
success && viewPatternsButtonRef?.current?.focus() |
| 33 |
}, [success]) |
| 34 |
|
| 35 |
const logout = () => { |
| 36 |
setApiKey('') |
| 37 |
useUserStore.setState({ apiKey: '' }) |
| 38 |
setTimeout(() => { |
| 39 |
licenseKeyRef?.current?.focus() |
| 40 |
}, 0) |
| 41 |
} |
| 42 |
|
| 43 |
const confirmKey = async (event) => { |
| 44 |
event.preventDefault() |
| 45 |
setIsWorking(true) |
| 46 |
setFeedback('') |
| 47 |
const { token, error, exception, message } = await UserApi.authenticate( |
| 48 |
email, |
| 49 |
apiKey, |
| 50 |
) |
| 51 |
|
| 52 |
if (typeof message !== 'undefined') { |
| 53 |
setFeedbackType('error') |
| 54 |
setIsWorking(false) |
| 55 |
setFeedback( |
| 56 |
message?.length |
| 57 |
? message |
| 58 |
: 'Error: Are you interacting with the wrong server?', |
| 59 |
) |
| 60 |
return |
| 61 |
} |
| 62 |
|
| 63 |
if (error || exception) { |
| 64 |
setFeedbackType('error') |
| 65 |
setIsWorking(false) |
| 66 |
setFeedback(error?.length ? error : exception) |
| 67 |
return |
| 68 |
} |
| 69 |
|
| 70 |
if (!token || typeof token !== 'string') { |
| 71 |
setFeedbackType('error') |
| 72 |
setIsWorking(false) |
| 73 |
setFeedback(__('Something went wrong', 'extendify')) |
| 74 |
return |
| 75 |
} |
| 76 |
|
| 77 |
setFeedbackType('success') |
| 78 |
setFeedback('Success!') |
| 79 |
setSuccess(true) |
| 80 |
setIsWorking(false) |
| 81 |
useUserStore.setState({ |
| 82 |
email: email, |
| 83 |
apiKey: token, |
| 84 |
}) |
| 85 |
} |
| 86 |
|
| 87 |
if (success) { |
| 88 |
return ( |
| 89 |
<section className="space-y-6 p-6 text-center flex flex-col items-center"> |
| 90 |
<Icon icon={successIcon} size={148} /> |
| 91 |
<p className="text-center text-lg font-semibold m-0 text-extendify-black"> |
| 92 |
{sprintf( |
| 93 |
// translators: %s: The name of the plugin, Extendify. |
| 94 |
__("You've signed in to %s", 'extendify'), |
| 95 |
'Extendify', |
| 96 |
)} |
| 97 |
</p> |
| 98 |
<Button |
| 99 |
ref={viewPatternsButtonRef} |
| 100 |
className="cursor-pointer rounded bg-extendify-main p-2 px-4 text-center text-white" |
| 101 |
onClick={actionCallback}> |
| 102 |
{__('View patterns', 'extendify')} |
| 103 |
</Button> |
| 104 |
</section> |
| 105 |
) |
| 106 |
} |
| 107 |
|
| 108 |
if (loggedIn) { |
| 109 |
return ( |
| 110 |
<section className="w-full space-y-6 p-6"> |
| 111 |
<p className="text-base m-0 text-extendify-black"> |
| 112 |
{__('Account', 'extendify')} |
| 113 |
</p> |
| 114 |
<div className="flex items-center justify-between"> |
| 115 |
<div className="-ml-2 flex items-center space-x-2"> |
| 116 |
<Icon icon={user} size={48} /> |
| 117 |
<p className="text-extendify-black"> |
| 118 |
{email?.length |
| 119 |
? email |
| 120 |
: __('Logged In', 'extendify')} |
| 121 |
</p> |
| 122 |
</div> |
| 123 |
{devMode && ( |
| 124 |
<Button |
| 125 |
className="cursor-pointer rounded bg-extendify-main px-4 py-3 text-center text-white hover:bg-extendify-main-dark" |
| 126 |
onClick={logout}> |
| 127 |
{__('Sign out', 'extendify')} |
| 128 |
</Button> |
| 129 |
)} |
| 130 |
</div> |
| 131 |
</section> |
| 132 |
) |
| 133 |
} |
| 134 |
|
| 135 |
return ( |
| 136 |
<section className="space-y-6 p-6 text-left"> |
| 137 |
<div> |
| 138 |
<p className="text-center text-lg font-semibold m-0 text-extendify-black"> |
| 139 |
{__('Sign in to Extendify', 'extendify')} |
| 140 |
</p> |
| 141 |
<p |
| 142 |
className="space-x-1 text-center text-sm m-0 text-extendify-gray" |
| 143 |
dangerouslySetInnerHTML={{ |
| 144 |
__html: safeHTML( |
| 145 |
sprintf( |
| 146 |
// translators: %s: The partners@extendify.com email address. |
| 147 |
__( |
| 148 |
"Don't have an account? Ask your hosting provider to reach out to %s.", |
| 149 |
'extendify', |
| 150 |
), |
| 151 |
'<a href="mailto:partners@extendify.com">partners@extendify.com</a>', |
| 152 |
), |
| 153 |
), |
| 154 |
}} |
| 155 |
/> |
| 156 |
</div> |
| 157 |
<form |
| 158 |
onSubmit={confirmKey} |
| 159 |
className="flex flex-col items-center justify-center space-y-2"> |
| 160 |
<div className="flex items-center"> |
| 161 |
<label className="sr-only" htmlFor="extendify-login-email"> |
| 162 |
{__('Email address', 'extendify')} |
| 163 |
</label> |
| 164 |
<input |
| 165 |
ref={initialFocus} |
| 166 |
id="extendify-login-email" |
| 167 |
name="extendify-login-email" |
| 168 |
style={{ minWidth: '320px' }} |
| 169 |
type="email" |
| 170 |
className="w-full rounded border-2 p-2" |
| 171 |
placeholder={__('Email address', 'extendify')} |
| 172 |
value={email.length ? email : ''} |
| 173 |
onChange={(event) => setEmail(event.target.value)} |
| 174 |
/> |
| 175 |
</div> |
| 176 |
<div className="flex items-center"> |
| 177 |
<label |
| 178 |
className="sr-only" |
| 179 |
htmlFor="extendify-login-license"> |
| 180 |
{__('License key', 'extendify')} |
| 181 |
</label> |
| 182 |
<input |
| 183 |
ref={licenseKeyRef} |
| 184 |
id="extendify-login-license" |
| 185 |
name="extendify-login-license" |
| 186 |
style={{ minWidth: '320px' }} |
| 187 |
type="text" |
| 188 |
className="w-full rounded border-2 p-2" |
| 189 |
placeholder={__('License key', 'extendify')} |
| 190 |
value={apiKey} |
| 191 |
onChange={(event) => setApiKey(event.target.value)} |
| 192 |
/> |
| 193 |
</div> |
| 194 |
<div className="flex justify-center pt-2"> |
| 195 |
<button |
| 196 |
type="submit" |
| 197 |
className="relative flex w-72 max-w-full cursor-pointer justify-center rounded bg-extendify-main p-2 py-3 text-center text-base text-white hover:bg-extendify-main-dark "> |
| 198 |
<span>{__('Sign in', 'extendify')}</span> |
| 199 |
{isWorking && ( |
| 200 |
<div className="absolute right-2.5"> |
| 201 |
<Spinner /> |
| 202 |
</div> |
| 203 |
)} |
| 204 |
</button> |
| 205 |
</div> |
| 206 |
{feedback && ( |
| 207 |
<div |
| 208 |
className={classNames({ |
| 209 |
'border-gray-900 text-gray-900': |
| 210 |
feedbackType === 'info', |
| 211 |
'border-wp-alert-red text-wp-alert-red': |
| 212 |
feedbackType === 'error', |
| 213 |
'border-extendify-main text-extendify-main': |
| 214 |
feedbackType === 'success', |
| 215 |
})}> |
| 216 |
{feedback} |
| 217 |
</div> |
| 218 |
)} |
| 219 |
</form> |
| 220 |
</section> |
| 221 |
) |
| 222 |
} |
| 223 |
|