diff --git a/src/components/Connection/Connection.spec.tsx b/src/components/Connection/Connection.spec.tsx index f2d7e23..9e16296 100644 --- a/src/components/Connection/Connection.spec.tsx +++ b/src/components/Connection/Connection.spec.tsx @@ -1,12 +1,6 @@ import { act, fireEvent, render } from '@testing-library/react' import { Connection } from './Connection' -import { - SHOW_MORE_BUTTON_TEST_ID, - SOCIAL_PRIMARY_TEST_ID, - SOCIAL_SECONDARY_TEST_ID, - WEB3_PRIMARY_TEST_ID, - WEB3_SECONDARY_TEST_ID -} from './constants' +import { EXTRA_TEST_ID, PRIMARY_TEST_ID, SECONDARY_TEST_ID, SHOW_MORE_BUTTON_TEST_ID } from './constants' import { ConnectionOptionType, ConnectionProps } from './Connection.types' function renderConnection(props: Partial) { @@ -36,50 +30,39 @@ function renderConnection(props: Partial) { let screen: ReturnType describe('when rendering the component', () => { - let socialOptions: ConnectionProps['socialOptions'] | undefined - let web3Options: ConnectionProps['web3Options'] | undefined + let connectionOptions: ConnectionProps['connectionOptions'] let onConnect: jest.Mock - describe('and there are no social options', () => { - beforeEach(() => { - socialOptions = undefined - screen = renderConnection({ socialOptions }) - }) - - it('should not render the primary social option', () => { - const { queryByTestId } = screen - expect(queryByTestId(SOCIAL_PRIMARY_TEST_ID)).not.toBeInTheDocument() - }) - }) - - describe('and there are social options', () => { + describe('and there are primary, secondary and extra options', () => { beforeEach(() => { onConnect = jest.fn() - socialOptions = { + connectionOptions = { primary: ConnectionOptionType.GOOGLE, - secondary: [ConnectionOptionType.APPLE, ConnectionOptionType.X, ConnectionOptionType.DISCORD] + secondary: ConnectionOptionType.APPLE, + extraOptions: [ConnectionOptionType.X, ConnectionOptionType.DISCORD] } - screen = renderConnection({ socialOptions, onConnect }) + screen = renderConnection({ connectionOptions, onConnect }) }) - it('should render the primary social option', () => { + it('should render the primary and secondary option', () => { const { getByTestId } = screen - expect(getByTestId(SOCIAL_PRIMARY_TEST_ID)).toBeInTheDocument() + expect(getByTestId(PRIMARY_TEST_ID)).toBeInTheDocument() + expect(getByTestId(SECONDARY_TEST_ID)).toBeInTheDocument() }) it('should call the onConnect method prop when clicking the button', () => { const { getByTestId } = screen - fireEvent.click(getByTestId(`${SOCIAL_PRIMARY_TEST_ID}-${ConnectionOptionType.GOOGLE}-button`)) + fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.GOOGLE}-button`)) expect(onConnect).toHaveBeenCalledWith(ConnectionOptionType.GOOGLE) }) - it('should render all the secondary options', () => { + it('should render all the extra options', () => { const { getByTestId } = screen act(() => { fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID)) }) - socialOptions?.secondary.forEach(option => { - expect(getByTestId(`${SOCIAL_SECONDARY_TEST_ID}-${option}-button`)).toBeInTheDocument() + connectionOptions?.extraOptions?.forEach(option => { + expect(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)).toBeInTheDocument() }) }) @@ -88,25 +71,13 @@ describe('when rendering the component', () => { act(() => { fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID)) }) - socialOptions?.secondary.forEach(option => { - fireEvent.click(getByTestId(`${SOCIAL_SECONDARY_TEST_ID}-${option}-button`)) + connectionOptions?.extraOptions?.forEach(option => { + fireEvent.click(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)) expect(onConnect).toHaveBeenCalledWith(option) }) }) }) - describe('and there are no primary web3 options', () => { - beforeEach(() => { - socialOptions = undefined - screen = renderConnection({ socialOptions }) - }) - - it('should not render the primary web3 option', () => { - const { queryByTestId } = screen - expect(queryByTestId(WEB3_PRIMARY_TEST_ID)).not.toBeInTheDocument() - }) - }) - describe('and the user has metamask installed', () => { let oldEthereum: typeof window.ethereum @@ -116,12 +87,12 @@ describe('when rendering the component', () => { ...window.ethereum, isMetaMask: true } - web3Options = { + connectionOptions = { primary: ConnectionOptionType.METAMASK, - secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] + extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] } onConnect = jest.fn() - screen = renderConnection({ web3Options, onConnect }) + screen = renderConnection({ connectionOptions, onConnect }) }) afterEach(() => { @@ -130,7 +101,7 @@ describe('when rendering the component', () => { it('should call the onConnect method prop when clicking the button', () => { const { getByTestId } = screen - fireEvent.click(getByTestId(`${WEB3_PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`)) + fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`)) expect(onConnect).toHaveBeenCalledWith(ConnectionOptionType.METAMASK) }) }) @@ -141,12 +112,12 @@ describe('when rendering the component', () => { beforeEach(() => { oldEthereum = window.ethereum window.ethereum = undefined - web3Options = { + connectionOptions = { primary: ConnectionOptionType.METAMASK, - secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] + extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] } onConnect = jest.fn() - screen = renderConnection({ web3Options, onConnect }) + screen = renderConnection({ connectionOptions, onConnect }) }) afterEach(() => { @@ -155,33 +126,38 @@ describe('when rendering the component', () => { it('should not call the onConnect method prop when clicking the button', () => { const { getByTestId } = screen - fireEvent.click(getByTestId(`${WEB3_PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`)) + fireEvent.click(getByTestId(`${PRIMARY_TEST_ID}-${ConnectionOptionType.METAMASK}-button`)) expect(onConnect).not.toHaveBeenCalled() }) }) - describe('and there are web3 options', () => { + describe('and there are is not secondary options', () => { beforeEach(() => { onConnect = jest.fn() - web3Options = { + connectionOptions = { primary: ConnectionOptionType.METAMASK, - secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] + extraOptions: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.WALLET_CONNECT, ConnectionOptionType.COINBASE] } - screen = renderConnection({ web3Options, onConnect }) + screen = renderConnection({ connectionOptions, onConnect }) }) - it('should render the primary social option', () => { + it('should render the primary option', () => { const { getByTestId } = screen - expect(getByTestId(WEB3_PRIMARY_TEST_ID)).toBeInTheDocument() + expect(getByTestId(PRIMARY_TEST_ID)).toBeInTheDocument() + }) + + it('should not render the secondary', () => { + const { queryByTestId } = screen + expect(queryByTestId(SECONDARY_TEST_ID)).not.toBeInTheDocument() }) - it('should render all the secondary options', () => { + it('should render all the extra options', () => { const { getByTestId } = screen act(() => { fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID)) }) - web3Options?.secondary.forEach(option => { - expect(getByTestId(`${WEB3_SECONDARY_TEST_ID}-${option}-button`)).toBeInTheDocument() + connectionOptions?.extraOptions?.forEach(option => { + expect(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)).toBeInTheDocument() }) }) @@ -190,18 +166,41 @@ describe('when rendering the component', () => { act(() => { fireEvent.click(getByTestId(SHOW_MORE_BUTTON_TEST_ID)) }) - web3Options?.secondary.forEach(option => { - fireEvent.click(getByTestId(`${WEB3_SECONDARY_TEST_ID}-${option}-button`)) + connectionOptions?.extraOptions?.forEach(option => { + fireEvent.click(getByTestId(`${EXTRA_TEST_ID}-${option}-button`)) expect(onConnect).toHaveBeenCalledWith(option) }) }) }) - describe('and there are no web3 nor social secondary options', () => { + describe('and there are primary, but not secondary nor extra options', () => { + beforeEach(() => { + connectionOptions = { + primary: ConnectionOptionType.METAMASK + } + screen = renderConnection({ connectionOptions }) + }) + + it('should render the primary', () => { + const { queryByTestId } = screen + expect(queryByTestId(PRIMARY_TEST_ID)).toBeInTheDocument() + }) + + it('should not render the secondary', () => { + const { queryByTestId } = screen + expect(queryByTestId(SECONDARY_TEST_ID)).not.toBeInTheDocument() + }) + + it('should not render the more options button', () => { + const { queryByTestId } = screen + expect(queryByTestId(SHOW_MORE_BUTTON_TEST_ID)).not.toBeInTheDocument() + }) + }) + + describe('and there are no primary nor secondary nor extra options', () => { beforeEach(() => { - socialOptions = undefined - web3Options = undefined - screen = renderConnection({ socialOptions, web3Options }) + connectionOptions = undefined + screen = renderConnection({ connectionOptions }) }) it('should not render the more options button', () => { diff --git a/src/components/Connection/Connection.tsx b/src/components/Connection/Connection.tsx index a9307be..14212a4 100644 --- a/src/components/Connection/Connection.tsx +++ b/src/components/Connection/Connection.tsx @@ -4,15 +4,15 @@ import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon/Icon' import { Button } from 'decentraland-ui/dist/components/Button/Button' import logoSrc from '../../assets/images/logo.svg' import { ConnectionOption } from './ConnectionOption' +import { EXTRA_TEST_ID, PRIMARY_TEST_ID, SECONDARY_TEST_ID, SHOW_MORE_BUTTON_TEST_ID } from './constants' import { - SHOW_MORE_BUTTON_TEST_ID, - SOCIAL_PRIMARY_TEST_ID, - SOCIAL_SECONDARY_TEST_ID, - WEB3_PRIMARY_TEST_ID, - WEB3_SECONDARY_TEST_ID -} from './constants' -import { ConnectionOptionType, ConnectionProps, MetamaskEthereumWindow, connectionOptionTitles } from './Connection.types' + ConnectionOptionType, + ConnectionProps, + MetamaskEthereumWindow, + connectionOptionTitles, +} from './Connection.types' import styles from './Connection.module.css' +import { isSocialLogin } from '../Pages/LoginPage/utils' const Primary = ({ message, @@ -91,7 +91,7 @@ const defaultProps = { } export const Connection = (props: ConnectionProps): JSX.Element => { - const { i18n = defaultProps.i18n, onConnect, onLearnMore, socialOptions, web3Options, className, loadingOption } = props + const { i18n = defaultProps.i18n, onConnect, onLearnMore, connectionOptions, className, loadingOption } = props const [showMore, setShowMore] = useState(false) const handleShowMore = useCallback(() => { @@ -99,8 +99,39 @@ export const Connection = (props: ConnectionProps): JSX.Element => { }, [showMore]) const isMetamaskAvailable = (window.ethereum as MetamaskEthereumWindow)?.isMetaMask - const hasSocialSecondaryOptions = socialOptions && socialOptions.secondary && socialOptions.secondary.length > 0 - const hasWeb3SecondaryOptions = web3Options && web3Options.secondary && web3Options.secondary.length > 0 + const hasExtraOptions = connectionOptions?.extraOptions && connectionOptions.extraOptions.length > 0 + + const renderPrimary = (option: ConnectionOptionType, testId: string) => ( + + {i18n.socialMessage(
)} + onLearnMore(option)}> + Learn More + + + ) : ( + i18n.web3Message(element => ( + onLearnMore(option)}> + {element} + + )) + ) + } + > + <>{isSocialLogin(option) ? i18n.accessWith(option) : i18n.connectWith(option)} + + ) return (
@@ -108,48 +139,12 @@ export const Connection = (props: ConnectionProps): JSX.Element => {

{i18n.title}

{i18n.subtitle}

- {socialOptions ? ( - - {i18n.socialMessage(
)} - onLearnMore(socialOptions?.primary)}> - Learn More - - - } - > - <>{i18n.accessWith(socialOptions?.primary)} - - ) : null} - {web3Options ? ( - ( - onLearnMore(web3Options.primary)}> - {element} - - ))} - > - <>{i18n.connectWith(web3Options?.primary)} - - ) : null} + {connectionOptions && renderPrimary(connectionOptions.primary, PRIMARY_TEST_ID)} + {connectionOptions?.secondary && renderPrimary(connectionOptions.secondary, SECONDARY_TEST_ID)}
- {(hasWeb3SecondaryOptions || hasSocialSecondaryOptions) && ( + {hasExtraOptions && ( )} - {showMore && hasSocialSecondaryOptions && ( + {showMore && hasExtraOptions && connectionOptions.extraOptions && ( )} - {showMore && hasWeb3SecondaryOptions && ( - - )}
) diff --git a/src/components/Connection/Connection.types.ts b/src/components/Connection/Connection.types.ts index c06a5b3..2afc51f 100644 --- a/src/components/Connection/Connection.types.ts +++ b/src/components/Connection/Connection.types.ts @@ -42,13 +42,10 @@ export type ConnectionI18N = { export type ConnectionProps = { i18n?: ConnectionI18N - socialOptions?: { + connectionOptions?: { primary: ConnectionOptionType - secondary: ConnectionOptionType[] - } - web3Options?: { - primary: ConnectionOptionType - secondary: ConnectionOptionType[] + secondary?: ConnectionOptionType + extraOptions?: ConnectionOptionType[] } className?: string loadingOption?: ConnectionOptionType diff --git a/src/components/Connection/constants.ts b/src/components/Connection/constants.ts index c2a4772..9fb637a 100644 --- a/src/components/Connection/constants.ts +++ b/src/components/Connection/constants.ts @@ -1,5 +1,4 @@ -export const SOCIAL_PRIMARY_TEST_ID = 'social-primary-test-id' -export const SOCIAL_SECONDARY_TEST_ID = 'social-secondary-test-id' -export const WEB3_PRIMARY_TEST_ID = 'web3-primary-test-id' -export const WEB3_SECONDARY_TEST_ID = 'web3-secondary-test-id' +export const PRIMARY_TEST_ID = 'primary-test-id' +export const SECONDARY_TEST_ID = 'secondary-test-id' +export const EXTRA_TEST_ID = 'extra-options-test-id' export const SHOW_MORE_BUTTON_TEST_ID = 'show-more-button-test-id' diff --git a/src/components/Pages/LoginPage/LoginPage.tsx b/src/components/Pages/LoginPage/LoginPage.tsx index 8b0fa2f..ac363ad 100644 --- a/src/components/Pages/LoginPage/LoginPage.tsx +++ b/src/components/Pages/LoginPage/LoginPage.tsx @@ -24,7 +24,7 @@ import { ConnectionModal, ConnectionModalState } from '../../ConnectionModal' import { FeatureFlagsContext } from '../../FeatureFlagsProvider' import { MagicInformationModal } from '../../MagicInformationModal' import { WalletInformationModal } from '../../WalletInformationModal' -import { getIdentitySignature, connectToProvider, isSocialLogin, fromConnectionOptionToProviderType, getIsMobile } from './utils' +import { getIdentitySignature, connectToProvider, isSocialLogin, fromConnectionOptionToProviderType } from './utils' import styles from './LoginPage.module.css' const BACKGROUND_IMAGES = [Image1, Image2, Image3, Image4, Image5, Image6, Image7, Image8, Image9, Image10] @@ -37,7 +37,6 @@ export const LoginPage = () => { const [showMagicLearnMore, setShowMagicLearnMore] = useState(false) const [showConnectionModal, setShowConnectionModal] = useState(false) const [currentConnectionType, setCurrentConnectionType] = useState() - const [isMobile] = useState(getIsMobile()) const { url: redirectTo, redirect } = useAfterLoginRedirection() const showGuestOption = redirectTo && new URL(redirectTo).pathname.includes('/play') const [currentBackgroundIndex, setCurrentBackgroundIndex] = useState(0) @@ -193,21 +192,7 @@ export const LoginPage = () => { onLearnMore={handleLearnMore} onConnect={handleOnConnect} loadingOption={currentConnectionType} - socialOptions={{ - primary: ConnectionOptionType.GOOGLE, - secondary: [ConnectionOptionType.DISCORD, ConnectionOptionType.APPLE, ConnectionOptionType.X] - }} - web3Options={ - isMobile - ? { - primary: ConnectionOptionType.WALLET_CONNECT, - secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.COINBASE] - } - : { - primary: ConnectionOptionType.METAMASK, - secondary: [ConnectionOptionType.FORTMATIC, ConnectionOptionType.COINBASE, ConnectionOptionType.WALLET_CONNECT] - } - } + connectionOptions={targetConfig.connectionOptions} /> {showGuestOption && (
diff --git a/src/components/Pages/LoginPage/utils.ts b/src/components/Pages/LoginPage/utils.ts index f2445cd..89f9d1e 100644 --- a/src/components/Pages/LoginPage/utils.ts +++ b/src/components/Pages/LoginPage/utils.ts @@ -106,7 +106,7 @@ export async function getIdentitySignature(address: string, provider: Provider): return identity } -export function getIsMobile() { +export function isMobile() { const userAgent = navigator.userAgent return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent) } diff --git a/src/components/Pages/RequestPage/RequestPage.tsx b/src/components/Pages/RequestPage/RequestPage.tsx index fefef14..31e4e3b 100644 --- a/src/components/Pages/RequestPage/RequestPage.tsx +++ b/src/components/Pages/RequestPage/RequestPage.tsx @@ -185,6 +185,10 @@ export const RequestPage = () => { throw new Error(result.error) } else { setView(View.VERIFY_SIGN_IN_COMPLETE) + + if (targetConfig.deepLink) { + window.location.href = targetConfig.deepLink + } } } catch (e) { setError(isErrorWithMessage(e) ? e.message : 'Unknown error') diff --git a/src/hooks/targetConfig.spec.ts b/src/hooks/targetConfig.spec.ts new file mode 100644 index 0000000..b261f12 --- /dev/null +++ b/src/hooks/targetConfig.spec.ts @@ -0,0 +1,109 @@ +import { useLocation } from 'react-router-dom' +import { renderHook } from '@testing-library/react-hooks' +import { ConnectionOptionType } from '../components/Connection' +import { isMobile } from '../components/Pages/LoginPage/utils' +import { _targetConfigs, useTargetConfig } from './targetConfig' + +jest.mock('react-router-dom') +jest.mock('../components/Pages/LoginPage/utils') + +describe('useTargetConfig', () => { + const mockLocation = { search: '' } + + beforeEach(() => { + jest.clearAllMocks() + ;(useLocation as jest.Mock).mockReturnValue(mockLocation) + }) + + it('returns default config when no targetConfigId is provided', () => { + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('default') + expect(config).toEqual(_targetConfigs.default) + }) + + it('returns ios config when targetConfigId=ios', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=ios' }) + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('ios') + expect(config).toEqual(_targetConfigs.ios) + }) + + it('returns android config when targetConfigId=android', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=android' }) + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('android') + expect(config).toEqual(_targetConfigs.android) + }) + + it('returns androidSocial config when targetConfigId=androidSocial', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=androidSocial' }) + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('androidSocial') + expect(config).toEqual(_targetConfigs.androidSocial) + }) + + it('returns androidWeb3 config when targetConfigId=androidWeb3', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=androidWeb3' }) + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('androidWeb3') + expect(config).toEqual(_targetConfigs.androidWeb3) + }) + + it('returns the mobile-adjusted config for default config on mobile', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=default' }) + ;(isMobile as jest.Mock).mockReturnValue(true) + + const { result } = renderHook(() => useTargetConfig()) + const [config] = result.current + + expect(config.connectionOptions.primary).toBe(ConnectionOptionType.GOOGLE) + expect(config.connectionOptions.secondary).toBe(ConnectionOptionType.WALLET_CONNECT) + expect(config.connectionOptions.extraOptions).not.toContain(ConnectionOptionType.WALLET_CONNECT) + expect(config.connectionOptions.extraOptions).not.toContain(ConnectionOptionType.METAMASK) + }) + + it('applies mobile adjustments for all targetConfigId configurations', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=alternative' }) + ;(isMobile as jest.Mock).mockReturnValue(true) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('alternative') + expect(config.connectionOptions.primary).toBe(ConnectionOptionType.GOOGLE) + expect(config.connectionOptions.secondary).toBe(ConnectionOptionType.WALLET_CONNECT) + expect(config.connectionOptions.extraOptions).not.toContain(ConnectionOptionType.WALLET_CONNECT) + expect(config.connectionOptions.extraOptions).not.toContain(ConnectionOptionType.METAMASK) + }) + + it('returns default config when an invalid targetConfigId is provided', () => { + ;(useLocation as jest.Mock).mockReturnValue({ search: '?targetConfigId=invalid' }) + ;(isMobile as jest.Mock).mockReturnValue(false) + + const { result } = renderHook(() => useTargetConfig()) + const [config, targetConfigId] = result.current + + expect(targetConfigId).toBe('default') + expect(config).toEqual(_targetConfigs.default) + }) +}) diff --git a/src/hooks/targetConfig.ts b/src/hooks/targetConfig.ts index 40ccc50..d38a300 100644 --- a/src/hooks/targetConfig.ts +++ b/src/hooks/targetConfig.ts @@ -1,30 +1,140 @@ -import { useLocation } from 'react-router-dom' +import { useLocation, Location } from 'react-router-dom' +import { ConnectionOptionType } from '../components/Connection' +import { isMobile } from '../components/Pages/LoginPage/utils' + +type TargetConfigId = 'default' | 'alternative' | 'ios' | 'android' | 'androidSocial' | 'androidWeb3' + +type ConnectionOptions = { + primary: ConnectionOptionType + secondary?: ConnectionOptionType + extraOptions?: ConnectionOptionType[] +} -type TargetConfigId = 'default' | 'alternative' type TargetConfig = { skipSetup: boolean showWearablePreview: boolean explorerText: string + connectionOptions: ConnectionOptions + deepLink?: string +} + +const defaultConfig: TargetConfig = { + skipSetup: false, + showWearablePreview: true, + explorerText: 'Desktop App', + connectionOptions: { + primary: ConnectionOptionType.GOOGLE, + secondary: ConnectionOptionType.METAMASK, + extraOptions: [ + ConnectionOptionType.DISCORD, + ConnectionOptionType.APPLE, + ConnectionOptionType.X, + ConnectionOptionType.FORTMATIC, + ConnectionOptionType.COINBASE, + ConnectionOptionType.WALLET_CONNECT + ] + } +} + +const defaultMobileConfig: TargetConfig = { + ...defaultConfig, + skipSetup: true, + showWearablePreview: false, + explorerText: 'Mobile App', + deepLink: 'decentraland://' } -const targetConfigRecord: Record = { +const targetConfigs: Record = { default: { - skipSetup: false, - showWearablePreview: true, - explorerText: 'Desktop App' + ...defaultConfig }, alternative: { + ...defaultConfig, skipSetup: true, showWearablePreview: false, explorerText: 'Explorer' + }, + ios: { + ...defaultMobileConfig, + connectionOptions: { + primary: ConnectionOptionType.APPLE, + secondary: ConnectionOptionType.WALLET_CONNECT, + extraOptions: [ + ConnectionOptionType.GOOGLE, + ConnectionOptionType.DISCORD, + ConnectionOptionType.X, + ConnectionOptionType.FORTMATIC, + ConnectionOptionType.COINBASE + ] + } + }, + android: { + ...defaultMobileConfig, + connectionOptions: { + primary: ConnectionOptionType.GOOGLE, + secondary: ConnectionOptionType.WALLET_CONNECT, + extraOptions: [ + ConnectionOptionType.DISCORD, + ConnectionOptionType.APPLE, + ConnectionOptionType.X, + ConnectionOptionType.FORTMATIC, + ConnectionOptionType.COINBASE + ] + } + }, + androidSocial: { + ...defaultMobileConfig, + connectionOptions: { + primary: ConnectionOptionType.GOOGLE, + secondary: ConnectionOptionType.X, + extraOptions: [ConnectionOptionType.APPLE, ConnectionOptionType.DISCORD] + } + }, + androidWeb3: { + ...defaultMobileConfig, + connectionOptions: { + primary: ConnectionOptionType.WALLET_CONNECT + } } } +const adjustWeb3OptionsForMobile = (config: TargetConfig): TargetConfig => { + let { primary, secondary, extraOptions } = config.connectionOptions + + // Replace Metamask Extension for Wallet Connect on Mobile + if (primary === ConnectionOptionType.METAMASK) { + primary = ConnectionOptionType.WALLET_CONNECT + extraOptions = extraOptions?.filter(option => option !== ConnectionOptionType.WALLET_CONNECT) + } + + if (secondary === ConnectionOptionType.METAMASK) { + secondary = ConnectionOptionType.WALLET_CONNECT + extraOptions = extraOptions?.filter(option => option !== ConnectionOptionType.WALLET_CONNECT) + } + + extraOptions = extraOptions?.filter(option => option !== ConnectionOptionType.METAMASK) + + return { + ...config, + connectionOptions: { primary, secondary, extraOptions } + } +} + +// Exporting targetConfigs specifically for testing +export const _targetConfigs = targetConfigs + +const getTargetConfigId = (location: Location): TargetConfigId => { + const search = new URLSearchParams(location.search) + const targetConfigIdParam = search.get('targetConfigId') as TargetConfigId + return targetConfigIdParam in targetConfigs ? targetConfigIdParam : 'default' +} + export const useTargetConfig = (): [TargetConfig, TargetConfigId] => { const location = useLocation() - const search = new URLSearchParams(location.search) - const targetConfigIdSearchParam = search.get('targetConfigId') || 'default' - const targetConfigId = - targetConfigIdSearchParam && targetConfigIdSearchParam in targetConfigRecord ? (targetConfigIdSearchParam as TargetConfigId) : 'default' - return [targetConfigRecord[targetConfigId], targetConfigId] + const targetConfigId = getTargetConfigId(location) + let config = targetConfigs[targetConfigId] + if (isMobile()) { + config = adjustWeb3OptionsForMobile(config) + } + return [config, targetConfigId] }