Skip to content

Commit

Permalink
feat: refresh design (#191)
Browse files Browse the repository at this point in the history
Co-authored-by: Paolo <[email protected]>
  • Loading branch information
m1guelpf and pdtfh authored Dec 10, 2023
1 parent 44c1a1f commit fe7d166
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 183 deletions.
2 changes: 1 addition & 1 deletion examples/with-html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ <h1 id="heading">idkit-js</h1>

IDKit.init({
signal: 'test_signal',
app_id: 'app_staging_45068dca85829d2fd90e2dd6f0bff997',
app_id: 'app_ce4cb73cb75fc3b73b71ffb4de178410',
action: 'test-action',
bridge_url: 'https://wallet-bridge.stage-crypto.worldcoin.org',
action_description: 'Test action description',
Expand Down
79 changes: 24 additions & 55 deletions packages/react/src/components/IDKitWidget/BaseWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ import { shallow } from 'zustand/shallow'
import XMarkIcon from '../Icons/XMarkIcon'
import ErrorState from './States/ErrorState'
import { ConfigSource } from '@/types/config'
import LoadingIcon from '../Icons/LoadingIcon'
import * as Toast from '@radix-ui/react-toast'
import type { IDKitStore } from '@/store/idkit'
import PrivacyState from './States/PrivacyState'
import SuccessState from './States/SuccessState'
import WorldIDState from './States/WorldIDState'
import * as Dialog from '@radix-ui/react-dialog'
import type { WidgetProps } from '@/types/config'
import WorldcoinIcon from '../Icons/WorldcoinIcon'
import { Fragment, useEffect, useMemo } from 'react'
import WorldIDWordmark from '../Icons/WorldIDWordmark'
import { AnimatePresence, motion } from 'framer-motion'
import ArrowLongLeftIcon from '../Icons/ArrowLongLeftIcon'
import HostAppVerificationState from './States/HostAppVerificationState'
Expand Down Expand Up @@ -106,7 +105,7 @@ const IDKitWidget: FC<WidgetProps> = ({ children, ...config }) => {
}}
transition={{ layout: { duration: 0.15 } }}
className={
'relative z-50 w-full rounded-t-2xl bg-white pt-6 shadow focus:outline-none focus-visible:ring focus-visible:ring-purple-500/75 dark:bg-0d151d md:max-w-md md:rounded-b-2xl'
'relative z-50 flex min-h-[35rem] w-full flex-col rounded-t-2xl bg-white pt-6 shadow focus:outline-none focus-visible:ring focus-visible:ring-purple-500/75 dark:bg-0d151d md:max-w-md md:rounded-b-2xl'
}
>
<Toast.Provider>
Expand All @@ -123,62 +122,32 @@ const IDKitWidget: FC<WidgetProps> = ({ children, ...config }) => {
<ArrowLongLeftIcon className="w-4" />
</button>

<Dialog.Close className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 dark:bg-d3dfea/15 dark:text-white">
<Dialog.Close className="flex items-center justify-center rounded-full dark:text-white">
<XMarkIcon className="h-5 w-5" />
</Dialog.Close>
</div>
<div className="relative">
<motion.div
className="mx-6 mb-6"
layout="position"
animate={{
visibility: processing ? 'hidden' : 'visible',
}}
transition={{ layout: { duration: 0.15 } }}
>
<StageContent />
</motion.div>
<AnimatePresence>
{processing && (
<motion.div
className="absolute inset-0 flex items-center justify-center pb-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<LoadingIcon className="h-24 w-24" />
</motion.div>
)}
</AnimatePresence>
<div className="relative mx-6 mb-6 flex flex-1 flex-col items-center justify-center">
<StageContent />
</div>
<div className="flex items-center justify-between px-5 py-3 md:rounded-b-2xl">
<p className="flex items-center gap-1 text-sm text-9eafc0">
<span>{__('Verified with')}</span>
<a
href="https://worldcoin.org/world-id"
target="_blank"
rel="noreferrer"
>
<WorldIDWordmark className="h-4 text-0d151d dark:text-white" />
</a>
</p>
{stage != IDKITStage.PRIVACY ? (
<button
onClick={() => setStage(IDKITStage.PRIVACY)}
className="text-sm text-9eafc0 hover:underline"
>
{__('Privacy')}
</button>
) : (
<a
target="_blank"
href="https://docs.worldcoin.org/privacy"
className="text-sm text-9eafc0 hover:underline dark:text-9eafc0"
rel="noreferrer"
>
{__('Learn More')} &rarr;
</a>
)}
<div className="flex items-center justify-between border-t border-f5f5f7 p-7 md:rounded-b-2xl">
<a
href="https://worldcoin.org/world-id"
target="_blank"
rel="noreferrer"
className="flex items-center gap-1 text-sm text-9eafc0"
>
<WorldcoinIcon className="w-4 text-9eafc0 dark:text-white" />
<span>{__('Powered by Worldcoin')}</span>
</a>

<a
href="https://developer.worldcoin.org/privacy-statement"
target="_blank"
rel="noreferrer"
className="text-sm text-9eafc0 hover:underline"
>
{__('Terms & Privacy')}
</a>
</div>
</Toast.Provider>
</motion.div>
Expand Down
29 changes: 9 additions & 20 deletions packages/react/src/components/IDKitWidget/States/ErrorState.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { __ } from '@/lang'
import useIDKitStore from '@/store/idkit'
import type { IDKitStore } from '@/store/idkit'
import XMarkIcon from '@/components/Icons/XMarkIcon'
import ErrorIcon from '@/components/Icons/ErrorIcon'
import { AppErrorCodes } from '@worldcoin/idkit-core'

const getParams = ({ retryFlow, errorState }: IDKitStore) => ({ retryFlow, errorState })

const ERROR_TITLES: Partial<Record<AppErrorCodes, string>> = {
[AppErrorCodes.GenericError]: __('Something went wrong'),
[AppErrorCodes.FailedByHostApp]: __('Verification Declined'),
[AppErrorCodes.GenericError]: __('Verification Failed'),
}

const ERROR_MESSAGES: Record<AppErrorCodes, string> = {
[AppErrorCodes.ConnectionFailed]: __('Connection to the World App or identity wallet failed. Please try again.'),
[AppErrorCodes.VerificationRejected]: __('Verification request rejected in the World App.'),
[AppErrorCodes.ConnectionFailed]: __('Connection to your wallet failed. Please try again.'),
[AppErrorCodes.VerificationRejected]: __('You rejected the verification request.'),
[AppErrorCodes.MaxVerificationsReached]: __(
'You have already verified the maximum number of times for this action.'
),
[AppErrorCodes.CredentialUnavailable]: __('It seems you do not have the credential required by this app.'),
[AppErrorCodes.CredentialUnavailable]: __('It seems you do not have the verification level required by this app.'),
[AppErrorCodes.MalformedRequest]: __(
'There was a problem with this request. Please try again or contact the app owner.'
),
Expand All @@ -26,12 +26,10 @@ const ERROR_MESSAGES: Record<AppErrorCodes, string> = {
),
[AppErrorCodes.InclusionProofFailed]: __('There was an issue fetching your credential. Please try again.'),
[AppErrorCodes.InclusionProofPending]: __(
'Your credential is still being registered. Please wait a few minutes and try again.'
'Your identity is still being registered. Please wait a few minutes and try again.'
),
[AppErrorCodes.FailedByHostApp]: __('Verification failed by the app. Please contact the app owner for details.'),
[AppErrorCodes.UnexpectedResponse]: __(
'Unexpected response from the World App or identity wallet. Please try again.'
),
[AppErrorCodes.UnexpectedResponse]: __('Unexpected response from your wallet. Please try again.'),
[AppErrorCodes.GenericError]: __('Something unexpected went wrong. Please try again.'),
}

Expand All @@ -41,17 +39,13 @@ const ErrorState = () => {
return (
<div className="space-y-8">
<div className="-mt-5 flex items-center justify-center">
<div className="inline-flex aspect-square items-center justify-center rounded-full bg-red-100 p-5">
<div className="flex aspect-square items-center justify-center rounded-full bg-red-500 p-5">
<XMarkIcon className="w-8 text-white" />
</div>
</div>
<ErrorIcon className="w-24" />
</div>
<div>
<p className="text-center text-2xl font-semibold text-gray-900 dark:text-white">
{(errorState?.code && ERROR_TITLES[errorState.code]) || ERROR_TITLES[AppErrorCodes.GenericError]}
</p>
<p className="mt-2 text-center text-lg text-gray-400">
<p className="mx-auto mt-2 max-w-[14rem] text-center text-657080">
{/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */}
{errorState?.message || ERROR_MESSAGES[errorState?.code ?? AppErrorCodes.GenericError]}
</p>
Expand All @@ -65,11 +59,6 @@ const ErrorState = () => {
{__('Try Again')}
</button>
</div>
<div>
<p className="mt-4 text-xs text-gray-400">
{__('If you are the app owner, check the console for further details.')}
</p>
</div>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ const SuccessState = () => {
return (
<div className="space-y-6">
<div className="-mt-5 flex items-center justify-center">
<div className="inline-flex aspect-square items-center justify-center rounded-full bg-green-100 p-5">
<div className="flex aspect-square items-center justify-center rounded-full bg-green-500 p-5">
<CheckIcon className="w-8 text-white" />
</div>
</div>
<CheckIcon className="w-24 text-white" />
</div>
<div>
<p className="text-center text-2xl font-semibold text-gray-900 dark:text-white">{__('Success! 🎉')}</p>
{/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */}
<p className="text-70868f mt-2 text-center text-lg">{__('World ID verification was successful')}</p>
<p className="text-center text-2xl font-semibold text-gray-900 dark:text-white">
{__('Successfully verified')}
</p>
<p className="text-657080 mt-2 max-w-[14rem] text-center text-lg">
{__('Your World ID verification was successful')}
</p>
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useCallback, useState } from 'react'
import { AnimatePresence, motion } from 'framer-motion'
import LoadingIcon from '@/components/Icons/LoadingIcon'
import WorldcoinIcon from '@/components/Icons/WorldcoinIcon'
import QRPlaceholderIcon from '@/components/Icons/QRPlaceholderIcon'

type Props = {
qrData: string | null
Expand Down Expand Up @@ -80,7 +81,7 @@ const QRState: FC<Props> = ({ qrData, showQR, setShowQR }) => {
</div>
) : (
<div className="flex h-[244px] w-[244px] items-center justify-center">
<LoadingIcon className="h-[72px] w-[72px]" />
<QRPlaceholderIcon className="h-[244px] w-[244px] animate-pulse" />
</div>
)}
</div>
Expand Down
19 changes: 7 additions & 12 deletions packages/react/src/components/IDKitWidget/States/WorldIDState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,17 @@ const WorldIDState = () => {
}, [result, handleVerify, verificationState, setStage, errorCode, setErrorState, credential_types])

return (
<div className="-mt-6 space-y-6">
<div className="-mt-6 space-y-10">
<div>
<div className="mb-2 flex items-center justify-center">
<WorldcoinIcon className="h-8 text-0d151d dark:text-white" />
<div className="mb-4 flex items-center justify-center">
<WorldcoinIcon className="h-10 text-0d151d dark:text-white" />
</div>
<p className="text-center font-sora text-2xl font-semibold text-gray-900 dark:text-white">
{verificationState === VerificationState.WaitingForApp
? __('Confirm in World App')
: __('Continue with Worldcoin')}
{__('Verify with World ID')}
</p>
<p className="mt-3 text-center text-657080 dark:text-9eafc0 md:mt-2">
Please use your World App to scan the QR code
</p>
{verificationState === VerificationState.WaitingForApp && (
<p className="mt-3 text-center text-70868f dark:text-9eafc0 md:mt-2">
Please confirm the request in your app to continue.
</p>
)}
</div>
{verificationState === VerificationState.WaitingForApp ? (
<div className="flex items-center justify-center">
Expand All @@ -93,7 +89,6 @@ const WorldIDState = () => {
) : (
<QRState showQR={showQR} setShowQR={setShowQR} qrData={connectorURI} />
)}
{(media == 'desktop' || !showQR) && <AboutWorldID />}
</div>
)
}
Expand Down
48 changes: 44 additions & 4 deletions packages/react/src/components/Icons/CheckIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
import type { FC, HTMLAttributes } from 'react'

const CheckIcon: FC<HTMLAttributes<SVGElement>> = props => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 11 8">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100" {...props}>
<circle
cx="50"
cy="50"
r="49.375"
fill="url(#success-a)"
fillOpacity=".65"
stroke="url(#success-b)"
strokeWidth="1.25"
/>
<g filter="url(#success-c)">
<circle cx="50" cy="50" r="35" fill="#fff" />
<circle cx="50" cy="50" r="34.432" stroke="#CCEBCC" strokeWidth="1.136" />
</g>
<path
stroke="currentColor"
stroke="#090"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.6"
d="M9.868 1 3.771 7 1 4.273"
strokeWidth="3.75"
d="m41.25 52.5 4.375 4.375 13.125-13.75"
/>
<defs>
<linearGradient id="success-a" x1="50" x2="50" y1="0" y2="100" gradientUnits="userSpaceOnUse">
<stop stopColor="#99D699" />
<stop offset="1" stopColor="#99D699" stopOpacity="0" />
</linearGradient>
<linearGradient id="success-b" x1="50" x2="50" y1="0" y2="100" gradientUnits="userSpaceOnUse">
<stop stopColor="#99D699" />
<stop offset=".713" stopColor="#99D699" stopOpacity="0" />
</linearGradient>
<filter
id="success-c"
width="77.5"
height="77.5"
x="11.25"
y="13.125"
colorInterpolationFilters="sRGB"
filterUnits="userSpaceOnUse"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
<feOffset dy="1.875" />
<feGaussianBlur stdDeviation="1.875" />
<feColorMatrix values="0 0 0 0 0.8 0 0 0 0 0.921569 0 0 0 0 0.8 0 0 0 0.45 0" />
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_39_712" />
<feBlend in="SourceGraphic" in2="effect1_dropShadow_39_712" result="shape" />
</filter>
</defs>
</svg>
)

Expand Down
47 changes: 47 additions & 0 deletions packages/react/src/components/Icons/ErrorIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { FC, HTMLAttributes } from 'react'

const ErrorIcon: FC<HTMLAttributes<SVGElement>> = props => (
<svg {...props} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="49.375" fill="url(#a)" fillOpacity=".65" stroke="url(#b)" strokeWidth="1.25" />
<g filter="url(#c)">
<circle cx="50" cy="50" r="35" fill="#fff" />
<circle cx="50" cy="50" r="34.432" stroke="#FFC9AD" strokeWidth="1.136" />
</g>
<path
stroke="#FF4732"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="3.75"
d="m57.5 42.5-15 14.999m15 .001-15-14.999"
/>
<defs>
<linearGradient id="a" x1="50" x2="50" y1="0" y2="100" gradientUnits="userSpaceOnUse">
<stop stopColor="#FFA483" />
<stop offset="1" stopColor="#FFA483" stopOpacity="0" />
</linearGradient>
<linearGradient id="b" x1="50" x2="50" y1="0" y2="100" gradientUnits="userSpaceOnUse">
<stop stopColor="#FFA483" />
<stop offset=".713" stopColor="#FFA483" stopOpacity="0" />
</linearGradient>
<filter
id="c"
width="77.5"
height="77.5"
x="11.25"
y="13.125"
colorInterpolationFilters="sRGB"
filterUnits="userSpaceOnUse"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
<feOffset dy="1.875" />
<feGaussianBlur stdDeviation="1.875" />
<feColorMatrix values="0 0 0 0 1 0 0 0 0 0.788235 0 0 0 0 0.678431 0 0 0 0.45 0" />
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_39_740" />
<feBlend in="SourceGraphic" in2="effect1_dropShadow_39_740" result="shape" />
</filter>
</defs>
</svg>
)

export default ErrorIcon
Loading

0 comments on commit fe7d166

Please sign in to comment.