Skip to content

Commit

Permalink
refactor(pera): move Pera auto-connect to resumeSession
Browse files Browse the repository at this point in the history
Move auto-connect logic from constructor to `resumeSession` method in Pera wallet
implementations to fix SSR compatibility issues. Auto-connect now only triggers
when no other wallet is active and after client-side hydration.
  • Loading branch information
drichar committed Dec 7, 2024
1 parent ec250f9 commit 1709fb0
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 103 deletions.
120 changes: 73 additions & 47 deletions packages/use-wallet/src/__tests__/wallets/pera.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,79 @@ describe('PeraWallet', () => {
expect(store.state.wallets[WalletId.PERA]).toBeUndefined()
expect(wallet.isConnected).toBe(false)
})

describe('auto-connect in Pera browser', () => {
let mockUserAgent: string

beforeEach(() => {
mockUserAgent = ''
vi.clearAllMocks()

vi.stubGlobal('window', {
navigator: {
get userAgent() {
return mockUserAgent
}
}
})
})

afterEach(() => {
vi.unstubAllGlobals()
})

it('should attempt auto-connect in Pera browser when no session exists and no active wallet', async () => {
mockUserAgent = 'pera/1.0.0'
mockPeraWallet.connect.mockResolvedValueOnce([account1.address])

await wallet.resumeSession()

expect(mockPeraWallet.connect).toHaveBeenCalled()
expect(store.state.wallets[WalletId.PERA]).toBeDefined()
expect(mockLogger.info).toHaveBeenCalledWith('Auto-connect successful')
})

it('should not attempt auto-connect if another wallet is active', async () => {
mockUserAgent = 'pera/1.0.0'

// Set up another active wallet
store.setState((state) => ({
...state,
activeWallet: WalletId.DEFLY,
wallets: {
[WalletId.DEFLY]: {
accounts: [account2],
activeAccount: account2
}
}
}))

await wallet.resumeSession()

expect(mockPeraWallet.connect).not.toHaveBeenCalled()
expect(mockLogger.info).toHaveBeenCalledWith('No session to resume')
})

it('should not attempt auto-connect in other browsers', async () => {
mockUserAgent = 'chrome/1.0.0'

await wallet.resumeSession()

expect(mockPeraWallet.connect).not.toHaveBeenCalled()
expect(mockLogger.info).toHaveBeenCalledWith('No session to resume')
})

it('should handle auto-connect failure gracefully', async () => {
mockUserAgent = 'pera/1.0.0'
mockPeraWallet.connect.mockRejectedValueOnce(new Error('Connect failed'))

await wallet.resumeSession()

expect(mockPeraWallet.connect).toHaveBeenCalled()
expect(mockLogger.warn).toHaveBeenCalledWith('Auto-connect failed:', 'Connect failed')
expect(store.state.wallets[WalletId.PERA]).toBeUndefined()
})
})
})

describe('setActive', () => {
Expand Down Expand Up @@ -541,53 +614,6 @@ describe('PeraWallet', () => {
})
})

describe('autoConnect', () => {
let mockUserAgent: string

beforeEach(() => {
mockUserAgent = ''
vi.clearAllMocks()

vi.stubGlobal('window', {
navigator: {
get userAgent() {
return mockUserAgent
}
}
})
})

afterEach(() => {
vi.unstubAllGlobals()
})

it('should attempt auto-connect in Pera browser', () => {
mockUserAgent = 'pera/1.0.0'

// Mock the private method before creating the wallet
vi.spyOn(PeraWallet.prototype as any, 'autoConnect')
.mockReset()
.mockImplementation(() => Promise.resolve())

createWalletWithStore(store)

expect(PeraWallet.prototype['autoConnect']).toHaveBeenCalled()
})

it('should not attempt auto-connect in other browsers', () => {
mockUserAgent = 'chrome/1.0.0'

// Mock the private method before creating the wallet
vi.spyOn(PeraWallet.prototype as any, 'autoConnect')
.mockReset()
.mockImplementation(() => Promise.resolve())

createWalletWithStore(store)

expect(PeraWallet.prototype['autoConnect']).not.toHaveBeenCalled()
})
})

describe('signing transactions', () => {
// Connected accounts
const connectedAcct1 = '7ZUECA7HFLZTXENRV24SHLU4AVPUTMTTDUFUBNBD64C73F3UHRTHAIOF6Q'
Expand Down
90 changes: 58 additions & 32 deletions packages/use-wallet/src/__tests__/wallets/pera2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,52 +332,78 @@ describe('PeraWallet', () => {
expect(store.state.wallets[WalletId.PERA2]).toBeUndefined()
expect(wallet.isConnected).toBe(false)
})
})

describe('autoConnect', () => {
let mockUserAgent: string
describe('auto-connect in Pera browser', () => {
let mockUserAgent: string

beforeEach(() => {
mockUserAgent = ''
vi.clearAllMocks()
beforeEach(() => {
mockUserAgent = ''
vi.clearAllMocks()

vi.stubGlobal('window', {
navigator: {
get userAgent() {
return mockUserAgent
vi.stubGlobal('window', {
navigator: {
get userAgent() {
return mockUserAgent
}
}
}
})
})
})

afterEach(() => {
vi.unstubAllGlobals()
})
afterEach(() => {
vi.unstubAllGlobals()
})

it('should attempt auto-connect in Pera browser', () => {
mockUserAgent = 'pera/1.0.0'
it('should attempt auto-connect in Pera browser when no session exists and no active wallet', async () => {
mockUserAgent = 'pera/1.0.0'
mockPeraWallet.connect.mockResolvedValueOnce([account1.address])

// Mock the private method before creating the wallet
vi.spyOn(PeraWallet.prototype as any, 'autoConnect')
.mockReset()
.mockImplementation(() => Promise.resolve())
await wallet.resumeSession()

createWalletWithStore(store)
expect(mockPeraWallet.connect).toHaveBeenCalled()
expect(store.state.wallets[WalletId.PERA2]).toBeDefined()
expect(mockLogger.info).toHaveBeenCalledWith('Auto-connect successful')
})

expect(PeraWallet.prototype['autoConnect']).toHaveBeenCalled()
})
it('should not attempt auto-connect if another wallet is active', async () => {
mockUserAgent = 'pera/1.0.0'

// Set up another active wallet
store.setState((state) => ({
...state,
activeWallet: WalletId.DEFLY,
wallets: {
[WalletId.DEFLY]: {
accounts: [account2],
activeAccount: account2
}
}
}))

it('should not attempt auto-connect in other browsers', () => {
mockUserAgent = 'chrome/1.0.0'
await wallet.resumeSession()

// Mock the private method before creating the wallet
vi.spyOn(PeraWallet.prototype as any, 'autoConnect')
.mockReset()
.mockImplementation(() => Promise.resolve())
expect(mockPeraWallet.connect).not.toHaveBeenCalled()
expect(mockLogger.info).toHaveBeenCalledWith('No session to resume')
})

it('should not attempt auto-connect in other browsers', async () => {
mockUserAgent = 'chrome/1.0.0'

createWalletWithStore(store)
await wallet.resumeSession()

expect(mockPeraWallet.connect).not.toHaveBeenCalled()
expect(mockLogger.info).toHaveBeenCalledWith('No session to resume')
})

expect(PeraWallet.prototype['autoConnect']).not.toHaveBeenCalled()
it('should handle auto-connect failure gracefully', async () => {
mockUserAgent = 'pera/1.0.0'
mockPeraWallet.connect.mockRejectedValueOnce(new Error('Connect failed'))

await wallet.resumeSession()

expect(mockPeraWallet.connect).toHaveBeenCalled()
expect(mockLogger.warn).toHaveBeenCalledWith('Auto-connect failed:', 'Connect failed')
expect(store.state.wallets[WalletId.PERA2]).toBeUndefined()
})
})
})

Expand Down
22 changes: 15 additions & 7 deletions packages/use-wallet/src/wallets/pera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ export class PeraWallet extends BaseWallet {
super({ id, metadata, getAlgodClient, store, subscribe, networks })
this.options = options
this.store = store

if (typeof window !== 'undefined' && window.navigator) {
const isPeraDiscover = window.navigator.userAgent.includes('pera')
if (isPeraDiscover) {
void this.autoConnect()
}
}
}

static defaultMetadata = {
Expand Down Expand Up @@ -154,6 +147,21 @@ export class PeraWallet extends BaseWallet {
const state = this.store.state
const walletState = state.wallets[this.id]

// Check for Pera Discover browser and auto-connect if no other wallet is active
if (typeof window !== 'undefined' && window.navigator) {
const isPeraDiscover = window.navigator.userAgent.includes('pera')
if (isPeraDiscover && !walletState && !state.activeWallet) {
this.logger.info('Pera Discover browser detected, attempting auto-connect...')
try {
await this.connect()
this.logger.info('Auto-connect successful')
return
} catch (error: any) {
this.logger.warn('Auto-connect failed:', error.message)
}
}
}

// No session to resume
if (!walletState) {
this.logger.info('No session to resume')
Expand Down
32 changes: 15 additions & 17 deletions packages/use-wallet/src/wallets/pera2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,30 +53,13 @@ export class PeraWallet extends BaseWallet {
}
this.options = options
this.store = store

if (typeof window !== 'undefined' && window.navigator) {
const isPeraDiscover = window.navigator.userAgent.includes('pera')
if (isPeraDiscover) {
void this.autoConnect()
}
}
}

static defaultMetadata = {
name: 'Pera',
icon: ICON
}

private async autoConnect(): Promise<void> {
this.logger.info('Pera Discover browser detected, auto connecting...')
try {
await this.connect()
this.logger.info('Auto-connect successful')
} catch (error: any) {
this.logger.warn('Auto-connect failed:', error.message)
}
}

private async initializeClient(): Promise<PeraWalletConnect> {
this.logger.info('Initializing client...')
const module = await import('@perawallet/connect-beta')
Expand Down Expand Up @@ -136,6 +119,21 @@ export class PeraWallet extends BaseWallet {
const state = this.store.state
const walletState = state.wallets[this.id]

// Check for Pera Discover browser and auto-connect if no other wallet is active
if (typeof window !== 'undefined' && window.navigator) {
const isPeraDiscover = window.navigator.userAgent.includes('pera')
if (isPeraDiscover && !walletState && !state.activeWallet) {
this.logger.info('Pera Discover browser detected, attempting auto-connect...')
try {
await this.connect()
this.logger.info('Auto-connect successful')
return
} catch (error: any) {
this.logger.warn('Auto-connect failed:', error.message)
}
}
}

// No session to resume
if (!walletState) {
this.logger.info('No session to resume')
Expand Down

0 comments on commit 1709fb0

Please sign in to comment.