Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/chilly-poets-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@status-im/wallet': patch
'wallet': patch
---

add new wallet flow
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"mikestead.dotenv",
"bradlc.vscode-tailwindcss",
"vitest.explorer",
"github.vscode-github-actions"
"github.vscode-github-actions",
"eamodio.gitlens",
"github.vscode-pull-request-github"
]
}
5 changes: 1 addition & 4 deletions apps/portfolio/src/app/_components/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
'use client'

import { Navbar as NavbarBase } from '@status-im/wallet/components'
import { usePathname } from 'next/navigation'

const Navbar = () => {
const pathname = usePathname()

return <NavbarBase pathname={pathname} />
return <NavbarBase />
}

export { Navbar }
1 change: 1 addition & 0 deletions apps/wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@cardano-sdk/core": "^0.45.4",
"@cardano-sdk/crypto": "^0.2.3",
"@cardano-sdk/key-management": "^0.27.5",
"@hookform/resolvers": "^3.1.1",
"@radix-ui/react-dialog": "^1.1.1",
"@status-im/colors": "workspace:*",
"@status-im/components": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet/src/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ const apiRouter = router({
// )

const { id } = await keyStore.importKey(
Buffer.from(input.privateKey),
new Uint8Array(Buffer.from(input.privateKey)),
input.name,
input.password,
walletCore.CoinType.ethereum,
Expand Down
6 changes: 5 additions & 1 deletion apps/wallet/src/hooks/use-create-wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useMutation } from '@tanstack/react-query'
import { useMutation, useQueryClient } from '@tanstack/react-query'

import { useAPI } from '../providers/api-client'

export const useCreateWallet = () => {
const api = useAPI()
const queryClient = useQueryClient()

const { mutate, mutateAsync, ...result } = useMutation({
mutationKey: ['create-wallet'],
Expand All @@ -15,6 +16,9 @@ export const useCreateWallet = () => {

return mnemonic
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['wallets'] })
},
})

return {
Expand Down
6 changes: 5 additions & 1 deletion apps/wallet/src/hooks/use-import-wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useMutation } from '@tanstack/react-query'
import { useMutation, useQueryClient } from '@tanstack/react-query'

import { useAPI } from '../providers/api-client'

export const useImportWallet = () => {
const api = useAPI()
const queryClient = useQueryClient()

const { mutate, mutateAsync, ...result } = useMutation({
mutationKey: ['import-wallet'],
Expand All @@ -20,6 +21,9 @@ export const useImportWallet = () => {
name: 'Imported Wallet',
})
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['wallets'] })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't have distinct key from "create"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's invalidating query which has queryKey wallets and since we want to fetch same query in both cases, we need to invalidate same queryKey.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't queryKye support array and with it allow distinct and more descriptive keys https://tanstack.com/query/v5/docs/framework/react/guides/query-keys#simple-query-keys ?

},
})

return {
Expand Down
27 changes: 27 additions & 0 deletions apps/wallet/src/hooks/use-pin-extension.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useEffect, useState } from 'react'

export const usePinExtension = () => {
const [isPinExtension, setIsPinExtension] = useState<boolean>(false)

const handleClose = async () => {
setIsPinExtension(false)
await chrome.storage.local.set({ pinExtension: true })
}

useEffect(() => {
async function checkSettings() {
const storage = await chrome.storage.local.get(['pinExtension'])
if (storage.pinExtension) {
setIsPinExtension(false)
return
}

const settings = await chrome.action.getUserSettings()
setIsPinExtension(!settings.isOnToolbar)
}

checkSettings()
}, [])

return { isPinExtension, handleClose }
}
76 changes: 76 additions & 0 deletions apps/wallet/src/providers/wallet-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { createContext, useContext, useEffect, useState } from 'react'

import { useQuery } from '@tanstack/react-query'

import { apiClient } from './api-client'

import type { KeyStore } from '@trustwallet/wallet-core'

type Wallet = KeyStore.Wallet

type WalletContext = {
currentWallet: Wallet | null
wallets: Wallet[]
isLoading: boolean
hasWallets: boolean
setCurrentWallet: (id: Wallet['id']) => void
}

const WalletContext = createContext<WalletContext | undefined>(undefined)

export function useWallet() {
const context = useContext(WalletContext)
if (!context) {
throw new Error('useWallet must be used within WalletProvider')
}
return context
}

export function WalletProvider({ children }: { children: React.ReactNode }) {
const [selectedWalletId, setSelectedWalletId] = useState<string | null>(null)

const { data: wallets = [], isLoading } = useQuery({
queryKey: ['wallets'],
queryFn: () => apiClient.wallet.all.query(),
staleTime: 5 * 60 * 1000, // 5 minutes
})

const hasWallets = wallets.length > 0

const currentWallet = useMemo(() => {
if (!hasWallets) return null

if (selectedWalletId) {
const selectedWallet = wallets.find(
wallet => wallet.id === selectedWalletId,
)
if (selectedWallet) return selectedWallet
}

return wallets[0] || null
}, [hasWallets, selectedWalletId, wallets])

useEffect(() => {
if (hasWallets && !selectedWalletId && wallets[0]) {
setSelectedWalletId(wallets[0].id)
}
}, [hasWallets, selectedWalletId, wallets])

const setCurrentWallet = (id: string) => {
setSelectedWalletId(id)
}

const contextValue: WalletContext = {
currentWallet,
wallets,
isLoading,
hasWallets,
setCurrentWallet,
}

return (
<WalletContext.Provider value={contextValue}>
{children}
</WalletContext.Provider>
)
}
Binary file added apps/wallet/src/public/images/onboarding.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 15 additions & 12 deletions apps/wallet/src/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { TanStackRouterDevtools } from '@tanstack/router-devtools'
// import { QueryClientProvider } from '../../../portfolio/src/app/_providers/query-client-provider'
// import { StatusProvider } from '../../../portfolio/src/app/_providers/status-provider'
import { WagmiProvider } from '../../../portfolio/src/app/_providers/wagmi-provider'
import { WalletProvider } from '../providers/wallet-context'

// import { Inter } from 'next/font/google'
import type { QueryClient } from '@tanstack/react-query'
Expand Down Expand Up @@ -69,26 +70,28 @@ function RootComponent() {
<head>
<HeadContent />
</head>
<div id="app" className="isolate">
<div id="app" className="isolate" data-customisation="blue">
{/* <StatusProvider> */}
<WagmiProvider>
{/* <QueryClientProvider> */}
{/* <Suspense fallback={<div>Loading...</div>}> */}
{/* <AccountsProvider> */}
{/* <ConnectKitProvider> */}
<div className="flex min-h-[56px] items-center px-2">
<Navbar pathname={pathname} />
</div>
<div className="px-1">
<div className="flex-1 flex-col 2md:flex xl:pb-1">
<div className="flex h-[calc(100vh-60px)] flex-col overflow-clip rounded-[24px] bg-white-100">
{/* <OnboardingPage /> */}
<Outlet />
<WalletProvider>
<div className="flex min-h-[56px] items-center px-2">
<Navbar pathname={pathname} />
</div>
<div className="px-1">
<div className="flex-1 flex-col 2md:flex xl:pb-1">
<div className="flex h-[calc(100vh-60px)] flex-col overflow-y-auto rounded-[24px] bg-white-100">
{/* <OnboardingPage /> */}
<Outlet />
</div>
</div>
{/* <NotAllowed /> */}
<ToastContainer />
</div>
{/* <NotAllowed /> */}
<ToastContainer />
</div>
</WalletProvider>
{/* </ConnectKitProvider> */}
{/* </AccountsProvider> */}
{/* </Suspense> */}
Expand Down
10 changes: 9 additions & 1 deletion apps/wallet/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute, redirect } from '@tanstack/react-router'

import { apiClient } from '../providers/api-client'

export const Route = createFileRoute('/')({
component: RouteComponent,
Expand All @@ -9,6 +11,12 @@ export const Route = createFileRoute('/')({
},
],
}),
beforeLoad: async () => {
const wallets = await apiClient.wallet.all.query()
if (wallets && wallets.length > 0) {
throw redirect({ to: '/portfolio' })
} else throw redirect({ to: '/onboarding' })
},
})

function RouteComponent() {
Expand Down
18 changes: 13 additions & 5 deletions apps/wallet/src/routes/onboarding/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { createFileRoute, Outlet } from '@tanstack/react-router'
import { createFileRoute, Outlet, redirect } from '@tanstack/react-router'

import { apiClient } from '../../providers/api-client'

export const Route = createFileRoute('/onboarding')({
component: RouteComponent,
beforeLoad: () => {
// TODO: check if user is already onboarded
// throw redirect({ to: '/' })
beforeLoad: async () => {
const wallets = await apiClient.wallet.all.query()
if (wallets && wallets.length > 0) {
throw redirect({ to: '/portfolio' })
}
},
})

function RouteComponent() {
return <Outlet />
return (
<div className="m-auto min-h-[650px] w-full max-w-[440px] rounded-[24px] border border-neutral-5 p-5 shadow-2">
<Outlet />
</div>
)
}
59 changes: 45 additions & 14 deletions apps/wallet/src/routes/onboarding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createFileRoute, Link } from '@tanstack/react-router'
import { Button, Text } from '@status-im/components'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/onboarding/')({
component: RouteComponent,
Expand All @@ -13,19 +14,49 @@ export const Route = createFileRoute('/onboarding/')({

function RouteComponent() {
return (
<div className="grid gap-2">
<Link
to="/onboarding/new"
className="flex h-10 items-center rounded-12 bg-neutral-50 px-3 text-15 font-medium transition-colors active:bg-neutral-50 hover:bg-neutral-90"
>
Create a wallet
</Link>
<Link
to="/onboarding/import"
className="flex h-10 items-center rounded-12 bg-neutral-50 px-3 text-15 font-medium transition-colors active:bg-neutral-50 hover:bg-neutral-90"
>
I already have a wallet
</Link>
<div className="flex flex-col items-center gap-6 py-3 text-center">
<img
src="/images/onboarding.png"
alt="Onboarding"
className="size-[264px]"
/>
<div className="flex flex-col gap-4">
<Text size={40} weight="bold">
Your Wallet.
<br />
Your crypto.
</Text>
<Text size={15} color="$neutral-50">
Some awesome sub copy
</Text>
</div>
<div className="flex w-full max-w-[270px] flex-col gap-3">
<Button href="/onboarding/new">New wallet</Button>
<Button href="/onboarding/import" variant="grey">
Import wallet
</Button>
</div>
<Text size={13} color="$neutral-50">
By continuing you agree with Status
<br />
<a
href="https://github.com/status-im/status-software-legal-documents/blob/master/terms-of-use.md"
className="text-neutral-100"
target="_blank"
rel="noopenernoreferrer"
>
Terms of use
</a>{' '}
and{' '}
<a
href="https://github.com/status-im/status-software-legal-documents/blob/master/privacy-policy.md"
className="text-neutral-100"
target="_blank"
rel="noopener noreferrer"
>
Privacy policy
</a>
</Text>
</div>
)
}
Loading
Loading