Skip to content

Commit

Permalink
Merge pull request #2084 from Koniverse/koni/dev/issue-2073
Browse files Browse the repository at this point in the history
[Issue-2073] Add support mobile wallet
  • Loading branch information
saltict authored Oct 27, 2023
2 parents 00572c4 + 41a2d90 commit f3e7515
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 57 deletions.
1 change: 1 addition & 0 deletions packages/extension-koni-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"classnames": "^2.3.2",
"file-saver": "^2.0.5",
"i18next": "^21.9.2",
"mobile-detect": "^1.4.5",
"moment": "^2.29.4",
"phosphor-react": "^1.4.1",
"querystring": "^0.2.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ const Component: React.FC<Props> = ({ className }: Props) => {
};

useEffect(() => {
if (isPopup && isFirefox() && hasMasterPassword && !isOpenWindowRef.current) {
if (isPopup && isFirefox && hasMasterPassword && !isOpenWindowRef.current) {
isOpenWindowRef.current = true;
windowOpen({ allowedPath: '/accounts/new-seed-phrase' }).then(window.close).catch(console.log);
}
Expand Down
24 changes: 20 additions & 4 deletions packages/extension-koni-ui/src/Popup/Keyring/CreatePassword.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { AlertBox, InfoIcon, InstructionContainer, InstructionContentType, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components';
import { CREATE_RETURN, REQUEST_CREATE_PASSWORD_MODAL } from '@subwallet/extension-koni-ui/constants';
import { DEFAULT_ROUTER_PATH } from '@subwallet/extension-koni-ui/constants/router';
Expand Down Expand Up @@ -49,7 +50,15 @@ const Component: React.FC<Props> = ({ className }: Props) => {
const navigate = useNavigate();
const previousInfo = (useLocation().state || {}) as { prevPathname: string, prevState: any };

const { isNoAccount } = useSelector((state: RootState) => state.accountState);
const { accounts } = useSelector((state: RootState) => state.accountState);

const needMigrate = useMemo(
() => !!accounts
.filter((acc) => acc.address !== ALL_ACCOUNT_KEY && !acc.isExternal && !acc.isInjected)
.filter((acc) => !acc.isMasterPassword)
.length
, [accounts]
);

const [returnPath, setReturnStorage] = useLocalStorage(CREATE_RETURN, DEFAULT_ROUTER_PATH);

Expand Down Expand Up @@ -132,10 +141,16 @@ const Component: React.FC<Props> = ({ className }: Props) => {
}, [inactiveModal]);

useEffect(() => {
if (!isNoAccount && !isWebUI) {
if (needMigrate && !isWebUI) {
activeModal(REQUEST_CREATE_PASSWORD_MODAL);
}
}, [activeModal, isWebUI, isNoAccount]);
}, [activeModal, isWebUI, needMigrate]);

const onConfirmPasswordKeyPress = useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
form.submit();
}
}, [form]);

useFocusFormItem(form, FormFieldName.PASSWORD, !checkActive(REQUEST_CREATE_PASSWORD_MODAL));

Expand All @@ -151,7 +166,7 @@ const Component: React.FC<Props> = ({ className }: Props) => {
icon: FooterIcon
}
: undefined}
showBackButton={isNoAccount}
showBackButton={!needMigrate}
subHeaderIcons={[
{
icon: <InfoIcon />,
Expand Down Expand Up @@ -208,6 +223,7 @@ const Component: React.FC<Props> = ({ className }: Props) => {
statusHelpAsTooltip={true}
>
<Input
onKeyDown={onConfirmPasswordKeyPress}
placeholder={t('Confirm password')}
type='password'
/>
Expand Down
58 changes: 29 additions & 29 deletions packages/extension-koni-ui/src/Popup/Welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@
// SPDX-License-Identifier: Apache-2.0

import { Layout } from '@subwallet/extension-koni-ui/components';
import { CONNECT_EXTENSION, CREATE_RETURN, DEFAULT_ACCOUNT_TYPES, DEFAULT_ROUTER_PATH, SELECTED_ACCOUNT_TYPE } from '@subwallet/extension-koni-ui/constants';
import { AutoConnect, CONNECT_EXTENSION, CREATE_RETURN, DEFAULT_ACCOUNT_TYPES, DEFAULT_ROUTER_PATH, PREDEFINED_WALLETS, SELECTED_ACCOUNT_TYPE } from '@subwallet/extension-koni-ui/constants';
import { ATTACH_ACCOUNT_MODAL, CREATE_ACCOUNT_MODAL, IMPORT_ACCOUNT_MODAL, SELECT_ACCOUNT_MODAL } from '@subwallet/extension-koni-ui/constants/modal';
import { InjectContext } from '@subwallet/extension-koni-ui/contexts/InjectContext';
import useTranslation from '@subwallet/extension-koni-ui/hooks/common/useTranslation';
import { createAccountExternalV2 } from '@subwallet/extension-koni-ui/messaging';
import { RootState } from '@subwallet/extension-koni-ui/stores';
import { PhosphorIcon, ThemeProps } from '@subwallet/extension-koni-ui/types';
import { checkHasInjected } from '@subwallet/extension-koni-ui/utils/wallet';
import { Button, ButtonProps, Form, Icon, Image, Input, ModalContext } from '@subwallet/react-ui';
import CN from 'classnames';
import { FileArrowDown, PlusCircle, PuzzlePiece, Swatches, Wallet } from 'phosphor-react';
import { Callbacks, FieldData, RuleObject } from 'rc-field-form/lib/interface';
import React, { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react';
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import { useLocalStorage } from 'usehooks-ts';

import SocialGroup from '../components/SocialGroup';
import { EXTENSION_URL } from '../constants';
import { ScreenContext } from '../contexts/ScreenContext';
import useGetDefaultAccountName from '../hooks/account/useGetDefaultAccountName';
import usePreloadView from '../hooks/router/usePreloadView';
import { convertFieldToObject, openInNewTab, readOnlyScan, simpleCheckForm } from '../utils';
import { convertFieldToObject, isMobile, readOnlyScan, simpleCheckForm } from '../utils';

type Props = ThemeProps;

Expand All @@ -47,7 +47,7 @@ function Component ({ className }: Props): React.ReactElement<Props> {

const { activeModal, inactiveModal } = useContext(ModalContext);
const { isWebUI } = useContext(ScreenContext);
const { injected, loadingInject, selectWallet } = useContext(InjectContext);
const { enableInject, loadingInject, selectWallet } = useContext(InjectContext);

const { accounts, isNoAccount } = useSelector((root: RootState) => root.accountState);

Expand Down Expand Up @@ -156,12 +156,20 @@ function Component ({ className }: Props): React.ReactElement<Props> {
}
}, [reformatAttachAddress, autoGenAttachReadonlyAccountName, isAttachAddressEthereum, form, navigate]);

const items = useMemo((): WelcomeButtonItem[] => [
const buttonList = useMemo((): WelcomeButtonItem[] => [
{
description: t('Connect to your existing wallet'),
icon: PuzzlePiece,
id: CONNECT_EXTENSION,
schema: 'primary',
title: t('Connect wallet'),
loading: loadingInject
},
{
description: t('Create a new account with SubWallet'),
icon: PlusCircle,
id: CREATE_ACCOUNT_MODAL,
schema: isWebUI ? 'secondary' : 'primary',
schema: 'secondary',
title: t('Create a new account'),
loading: false
},
Expand All @@ -180,32 +188,14 @@ function Component ({ className }: Props): React.ReactElement<Props> {
schema: 'secondary',
title: t('Attach an account'),
loading: false
},
{
description: injected ? t('Connect to your existing extension wallet') : t('For management of your account keys'),
icon: PuzzlePiece,
id: CONNECT_EXTENSION,
schema: 'secondary',
title: injected ? t('Connect extension wallet') : t('Download SubWallet extension'),
loading: loadingInject
}
], [injected, isWebUI, t, loadingInject]);

const buttonList = useMemo(() => isWebUI ? items : items.slice(0, 3), [isWebUI, items]);
], [t, loadingInject]);

const openModal = useCallback((id: string) => {
return () => {
if (id === CONNECT_EXTENSION) {
if (injected) {
selectWallet();
} else {
openInNewTab(EXTENSION_URL)();
}

return;
}

if (id === CREATE_ACCOUNT_MODAL) {
selectWallet();
} else if (id === CREATE_ACCOUNT_MODAL) {
setSelectedAccountTypes(DEFAULT_ACCOUNT_TYPES);
navigate('/accounts/new-seed-phrase');
} else {
Expand All @@ -214,7 +204,7 @@ function Component ({ className }: Props): React.ReactElement<Props> {
}
};
}
, [activeModal, selectWallet, inactiveModal, injected, navigate, setSelectedAccountTypes]);
, [activeModal, selectWallet, inactiveModal, navigate, setSelectedAccountTypes]);

useEffect(() => {
if (!isNoAccount) {
Expand All @@ -223,6 +213,16 @@ function Component ({ className }: Props): React.ReactElement<Props> {
}
}, [isNoAccount, navigate, returnPath, setReturnStorage]);

useEffect(() => {
if (isMobile && !AutoConnect.ignore) {
const installedWallet = Object.values(PREDEFINED_WALLETS).find((w) => (w.supportMobile && checkHasInjected(w.key)));

if (installedWallet) {
enableInject(installedWallet.key);
}
}
}, [enableInject]);

return (
<Layout.Base
className={CN(className, '__welcome-layout-containter')}
Expand Down
4 changes: 3 additions & 1 deletion packages/extension-koni-ui/src/assets/logo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export const DefaultLogosMap: Record<string, string> = {
polkadot_js: '/images/projects/polkadot-js.png',
polkadot_js_mc: '/images/projects/polkadot-js-monochrome.svg',
talisman: '/images/projects/talisman.png',
talisman_mc: '/images/projects/talisman-monochrome.svg'
talisman_mc: '/images/projects/talisman-monochrome.svg',
nova: '/images/projects/nova-wallet.png',
nova_mc: '/images/projects/nova-wallet-monochrome.svg'
};

export const IconMap = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { PREDEFINED_WALLETS, SELECT_EXTENSION_MODAL } from '@subwallet/extension
import { InjectContext } from '@subwallet/extension-koni-ui/contexts/InjectContext';
import { useTranslation } from '@subwallet/extension-koni-ui/hooks';
import { Theme, ThemeProps } from '@subwallet/extension-koni-ui/types';
import { openInNewTab } from '@subwallet/extension-koni-ui/utils';
import { isAndroid, isFirefox, isIOS, isMobile, openInNewTab } from '@subwallet/extension-koni-ui/utils';
import { checkHasInjected } from '@subwallet/extension-koni-ui/utils/wallet';
import { Icon, Image, ModalContext, SettingItem, SwList } from '@subwallet/react-ui';
import CN from 'classnames';
import {CheckCircle, Download, DownloadSimple, MagnifyingGlass} from 'phosphor-react';
import { CheckCircle, DownloadSimple, MagnifyingGlass } from 'phosphor-react';
import React, { useCallback, useContext, useMemo } from 'react';
import styled, { useTheme } from 'styled-components';

Expand Down Expand Up @@ -68,12 +68,13 @@ const ExtensionItem: React.FC<ExtensionItemProps> = (props: ExtensionOptions) =>
name={title}
onPressItem={_onClick}
rightItem={(
!injected
!injected && !isMobile
? <Icon
className={'__download-icon'}
phosphorIcon={DownloadSimple}
size='sm'
weight='fill' />
weight='fill'
/>
: (isSelected &&
<Icon
className={'__selected-icon'}
Expand All @@ -98,14 +99,27 @@ const Component: React.FC<Props> = (props: Props) => {
const { selectedWallet } = useContext(InjectContext);

const options = useMemo((): ExtensionOptions[] =>
Object.values(PREDEFINED_WALLETS).map((item) => {
Object.values(PREDEFINED_WALLETS).filter((w) => ((isMobile && w.supportMobile) || (!isMobile && w.supportWeb))).map((item) => {
const { appStoreUrl, firefoxUrl, googlePlayUrl, icon, key, name, url } = item;
const isSelected = selectedWallet === key;
let installUrl = url;

// Detect the platform and set the install url
if (isAndroid && googlePlayUrl) {
installUrl = googlePlayUrl;
} else if (isIOS && appStoreUrl) {
installUrl = appStoreUrl;
} else if (isFirefox && firefoxUrl) {
installUrl = firefoxUrl;
}

return ({
url: item.url,
injected: checkHasInjected(item.key),
value: item.key,
icon: item.icon,
title: item.name,
isSelected: selectedWallet === item.key
url: installUrl,
injected: checkHasInjected(key),
value: key,
icon: icon,
title: name,
isSelected
});
}), [selectedWallet]);

Expand Down Expand Up @@ -137,7 +151,7 @@ const Component: React.FC<Props> = (props: Props) => {
className={CN(className)}
id={modalId}
onCancel={onClose}
title={t('Connect extension')}
title={t('Connect wallet')}
>
<SwList
displayRow
Expand Down
36 changes: 33 additions & 3 deletions packages/extension-koni-ui/src/constants/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import DefaultLogosMap from '@subwallet/extension-koni-ui/assets/logo';
import { InjectedWindow, WalletInfo } from '@subwallet/extension-koni-ui/types';

const currentOrigin = new URL(window.location.href).origin;
const encodedOrigin = encodeURIComponent(currentOrigin);

export const PREDEFINED_WALLETS: Record<string, WalletInfo> = {
SubWallet: {
description: '',
Expand All @@ -13,7 +16,12 @@ export const PREDEFINED_WALLETS: Record<string, WalletInfo> = {
key: 'SubWallet',
name: 'SubWallet',
substrateKey: 'subwallet-js',
url: 'https://subwallet.app/download.html'
url: 'https://chrome.google.com/webstore/detail/subwallet-polkadot-wallet/onhogfjeacnfoofkfgppdlbmlmnplgbn',
firefoxUrl: 'https://addons.mozilla.org/firefox/addon/subwallet/',
googlePlayUrl: `https://mobile.subwallet.app/browser?url=${encodedOrigin}`,
appStoreUrl: `https://mobile.subwallet.app/browser?url=${encodedOrigin}`,
supportWeb: true,
supportMobile: true
},
Talisman: {
description: '',
Expand All @@ -23,7 +31,10 @@ export const PREDEFINED_WALLETS: Record<string, WalletInfo> = {
key: 'Talisman',
name: 'Talisman',
substrateKey: 'talisman',
url: 'https://talisman.xyz/download/'
url: 'https://chrome.google.com/webstore/detail/talisman-polkadot-and-eth/fijngjgcjhjmmpcmkeiomlglpeiijkld',
firefoxUrl: 'https://addons.mozilla.org/firefox/addon/talisman-wallet-extension/',
supportWeb: true,
supportMobile: false
},
PolkadotJs: {
description: '',
Expand All @@ -33,8 +44,27 @@ export const PREDEFINED_WALLETS: Record<string, WalletInfo> = {
key: 'PolkadotJs',
name: 'Polkadot{.js}',
substrateKey: 'polkadot-js',
url: 'https://polkadot.js.org/extension/'
url: 'https://chrome.google.com/webstore/detail/polkadot%7Bjs%7D-extension/mopnmbcafieddcagagdcbnhejhlodfdd',
firefoxUrl: 'https://addons.mozilla.org/firefox/addon/polkadot-js-extension/',
supportWeb: true,
supportMobile: false
},
Nova: {
description: '',
evmKey: null,
icon: DefaultLogosMap.nova,
mcicon: DefaultLogosMap.nova_mc,
key: 'Nova',
name: 'Nova',
substrateKey: 'polkadot-js',
url: 'https://novawallet.io/',
googlePlayUrl: 'https://play.google.com/store/apps/details?id=io.novafoundation.nova.market',
appStoreUrl: 'https://apps.apple.com/us/app/nova-polkadot-kusama-wallet/id1597119355',
supportWeb: false,
supportMobile: true
}
};

export const win = window as Window & InjectedWindow;

export const AutoConnect = { ignore: false };
Loading

0 comments on commit f3e7515

Please sign in to comment.