Skip to content

Commit 4ee7093

Browse files
authored
fix: infinite rerender ImportWalletScreen (#37)
1 parent f8d6e08 commit 4ee7093

File tree

22 files changed

+158
-164
lines changed

22 files changed

+158
-164
lines changed

.github/workflows/node.js.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3+
4+
name: Node.js CI
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
strategy:
18+
matrix:
19+
node-version: [22.x]
20+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
- name: Use Node.js ${{ matrix.node-version }}
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
cache: 'npm'
29+
- run: npm i
30+
- run: npm run check
31+
- run: npm test

.husky/commit-msg

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
#!/usr/bin/env sh
22

33
message="$(cat $1)"
4-
requiredPattern="^(feat|fix|docs|style|refactor|test|chore)(\([^)]+\))?: .*$"
4+
requiredPattern="^(feat|fix|docs|style|refactor|test|chore|revert|build|perf|ops)(\([^)]+\))?: .*$"
55

6-
if ! [[ $message =~ $requiredPattern ]];
7-
then
6+
if ! [[ $message =~ $requiredPattern ]]; then
87
echo "-"
98
echo "-"
109
echo "-"

ARCHITECTURE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
12
# Folders
3+
4+
```txt
25
shared/ - code shared by both the mobile app and the browser extension.
36
src/
47
shared/ - code used by more than one feature.
58
navigation/
69
features/
710
NewWallet/ - create/import new wallet functionality
811
WalletHome/ - primary screen with balance and buttons for accessing other features
12+
```

CONTRIBUTING.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1-
### Pull requests
1+
# Contributing
2+
3+
## Pull requests
4+
25
- All changes go through pull requests, direct pushes to `main` are blocked.
36
- Open a PR from a feature branch (create it off `main`).
47
- We squash merge after review and passing checks.
58
- No force pushes or rebases onto main.
69
- Keep each pull request focused on a single logical change (one feature, fix, or refactor).
710

8-
### Commits
11+
## Commits
12+
913
- All commits must be signed (see [this](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)).
1014
- All commits must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
1115

12-
### General Conventions
16+
## General Conventions
17+
1318
- Don't create your own names (like "secret phrase" for describing mnemonic) to avoid confusion.
1419
- Keep terminology consistent (if the UI says "mnemonic", code must say "mnemonic" as well). One concept = one name everywhere.
1520
- Stick to English for all identifiers and comments. Follow proper English grammar rules as well.
1621
- Avoid magic numbers and strings - define them as constants with clear names instead.
1722
- Prefer clarity over brevity (`transactionId` is better than `txId`, unless the abbreviation is universally standard).
1823

19-
### Naming Conventions
24+
## Naming Conventions
25+
2026
- camelCase - functions, variables, hooks, utils
2127
- PascalCase - components, classes, types
2228
- UPPER_SNAKE_CASE - constants

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### Setup Instructions
22

3-
Before starting, make sure you’ve completed the [React Native environment setup](https://reactnative.dev/docs/set-up-your-environment).
3+
Before starting, make sure you have completed the [React Native environment setup](https://reactnative.dev/docs/set-up-your-environment).
44

55
1. Install dependencies:
66

babel.config.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
11
module.exports = {
22
presets: ['module:@react-native/babel-preset', 'nativewind/babel'],
3-
plugins: [
4-
[
5-
'module-resolver',
6-
{
7-
root: ['./'],
8-
// TODO Add the second alias for the package which is now in
9-
// 'shared' top level folder but first choose a name for the package
10-
alias: {
11-
"@": "./src",
12-
},
13-
},
14-
],
15-
],
163
};
Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,60 @@
1+
import { ReactNode } from 'react';
12
import { View } from 'react-native';
23
import { MnemonicInput } from './MnemonicInput';
3-
import { useEffect, useState } from 'react';
4-
import { useWalletScreenStyles } from '../../../shared/hooks/useWalletScreenStyle';
54

65
type MnemonicDisplayType = {
7-
mnemonicLength: number | null;
8-
onChange: (words: string[]) => void;
9-
initialWords?: string[];
6+
mnemonic: string[];
7+
setMnemonic: (words: string[]) => void;
108
};
119

1210
export function MnemonicDisplayInput({
13-
mnemonicLength,
14-
onChange,
15-
initialWords,
11+
mnemonic,
12+
setMnemonic,
1613
}: MnemonicDisplayType) {
17-
const styles = useWalletScreenStyles().mnemonic;
18-
19-
// Init state
20-
const [words, setWords] = useState<string[]>(() => {
21-
console.log('Initializing words with:', initialWords);
22-
return initialWords && mnemonicLength && initialWords.length === mnemonicLength
23-
? [...initialWords]
24-
: Array.from({ length: mnemonicLength || 0 }, () => '');
25-
});
26-
27-
// Sync with initialWords for pasted words
28-
useEffect(() => {
29-
if (
30-
mnemonicLength &&
31-
initialWords &&
32-
initialWords.length === mnemonicLength &&
33-
JSON.stringify(initialWords) !== JSON.stringify(words)
34-
) {
35-
console.log('Syncing words with initialWords:', initialWords);
36-
setWords([...initialWords]);
37-
}
38-
}, [initialWords, mnemonicLength, words]);
39-
40-
// If words was changed
41-
useEffect(() => {
42-
console.log('Words updated in effect:', words);
43-
if (onChange && mnemonicLength) onChange(words);
44-
}, [words, onChange, mnemonicLength]);
45-
46-
if (!mnemonicLength) return null;
4714

4815
// Two columns
49-
const half = mnemonicLength / 2;
16+
const half = mnemonic.length / 2;
17+
18+
if (half != mnemonic.length - half) {
19+
throw new Error("Critical error: half != mnemonicLength - half")
20+
}
5021

5122
const handleChange = (text: string, idx: number) => {
52-
const updated = [...words];
23+
const updated = [...mnemonic];
5324
updated[idx] = text;
54-
setWords(updated);
25+
setMnemonic(updated);
5526
};
5627

5728
return (
58-
<View className={`flex-row h-auto bg-custom_complement ${styles.container}`}>
59-
<View className={`flex-1 flex-col justify-between ${styles.columnMargin}`}>
29+
<View className='flex-row h-auto bg-custom_complement border-2 rounded-lg my-3 md:rounded-xl md:my-5'>
30+
<Column>
6031
{Array.from({ length: half }, (_, i) => (
6132
<MnemonicInput
6233
key={`left-${i}`}
6334
idx={i + 1}
6435
onChange={text => handleChange(text, i)}
65-
value={words[i] || ''}
36+
value={mnemonic[i] || ''}
6637
/>
6738
))}
68-
</View>
69-
<View className={`flex-1 flex-col justify-between ${styles.columnMargin}`}>
39+
</Column>
40+
<Column>
7041
{Array.from({ length: half }, (_, i) => (
7142
<MnemonicInput
7243
key={`right-${i}`}
7344
idx={i + 1 + half}
7445
onChange={text => handleChange(text, i + half)}
75-
value={words[i + half] || ''}
46+
value={mnemonic[i + half] || ''}
7647
/>
7748
))}
78-
</View>
49+
</Column>
7950
</View>
8051
);
8152
}
53+
54+
const Column = ({ children }: { children: ReactNode }) => {
55+
return (
56+
<View className='flex-1 flex-col justify-between m-1 md:m-2'>
57+
{children}
58+
</View>
59+
)
60+
}

src/features/NewWallet/screens/CreateWalletScreen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useNavigation } from '@react-navigation/native';
44
import { generateMnemonic } from '../../../../shared/crypto/mnemonic';
55
import { useEffect, useState } from 'react';
66
import { MnemonicDisplay } from '../components/MnemonicDisplay';
7-
import { CopyToClipboard as copyToClipboard } from '../../../shared/utils/copyToClipboard';
7+
import { copyToClipboard } from '../../../shared/utils/clipboard';
88
import Toggle from '../components/Toggle';
99
import Wrapper from '../../../shared/components/Wrapper';
1010
import { StepIndicator } from '../components/StepIndicator';

src/features/NewWallet/screens/ImportCreateScreen.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ type ImportCreateScreenNavigationProp = NativeStackNavigationProp<
1313

1414
export default function ImportCreateScreen() {
1515
const navigation = useNavigation<ImportCreateScreenNavigationProp>();
16-
1716
return (
1817
<Wrapper>
1918
<View className="flex-1 justify-center align-middle items-center">

src/features/NewWallet/screens/ImportWalletScreen.tsx

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useState, useEffect } from 'react';
55
import ScrollableWrapper from '../../../shared/components/ScrollableWrapper';
66
import { StepIndicator } from '../components/StepIndicator';
77
import { MnemonicDisplayInput } from '../components/MnemonicDisplayInput';
8-
import PasteMnemonic from '../../../shared/utils/pasteFromClipboard';
8+
import { pasteMnemonicFromClipboard } from '../../../shared/utils/clipboard';
99
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
1010
import { RootNavigatorTypeParamListType } from '../../../navigation/types';
1111
import TextWithFont from '../../../shared/components/TextWithFont';
@@ -16,27 +16,27 @@ type ImportWalletScreenNavigationProp = NativeStackNavigationProp<
1616
>;
1717

1818
type RouteParams = {
19-
mnemonicLength: number | null;
19+
mnemonicLength: number;
2020
};
2121

2222
export default function ImportWalletScreen() {
2323
const navigation = useNavigation<ImportWalletScreenNavigationProp>();
24+
2425
const route = useRoute();
2526
const { mnemonicLength } = route.params as RouteParams;
26-
const [mnemonic, setMnemonic] = useState<string[]>([]);
27+
28+
const [mnemonic, setMnemonic] = useState<string[]>(
29+
Array.from({ length: mnemonicLength }, () => '')
30+
);
2731

2832
// Check if all fields are filled in
2933
const isAllFilled =
3034
mnemonic.length === mnemonicLength && mnemonic.every(word => word.trim().length > 0);
3135

32-
useEffect(() => {
33-
console.log('Mnemonic updated:', mnemonic, 'All filled:', isAllFilled);
34-
}, [mnemonic, mnemonicLength, isAllFilled]);
35-
3636
const handleNext = async () => {
37-
console.log('User mnemonic words', mnemonic);
3837
if (isAllFilled) {
3938
try {
39+
// TODO remove this
4040
const fullMnemonic = mnemonic.join(' ');
4141
navigation.navigate('NameWalletScreen', { mnemonic: fullMnemonic });
4242
} catch (error) {
@@ -48,33 +48,30 @@ export default function ImportWalletScreen() {
4848
};
4949

5050
const handlePaste = async () => {
51-
await PasteMnemonic({ mnemonicLength, setMnemonic });
51+
await pasteMnemonicFromClipboard({ mnemonicLength, setMnemonic });
5252
};
5353

5454
return (
5555
<ScrollableWrapper>
5656
<View className="flex-col flex-1 mt-5 items-center">
5757
<View>
58-
<TextWithFont customStyle='text-xl md:text-2xl text-white text-center font-bold'>
58+
<TextWithFont customStyle='text-xl text-2xl text-white text-center font-bold'>
5959
Write your seed phrase
6060
</TextWithFont>
61-
<TextWithFont customStyle={`text-white text-center mt-2 text-sm md:text-lg`}>
61+
<TextWithFont customStyle='text-white text-center mt-2 text-sm md:text-lg'>
6262
Make sure no one can see your screen
6363
</TextWithFont>
6464
</View>
6565

6666
<MnemonicDisplayInput
67-
mnemonicLength={mnemonicLength}
68-
onChange={setMnemonic}
69-
initialWords={mnemonic}
67+
mnemonic={mnemonic}
68+
setMnemonic={setMnemonic}
7069
/>
7170

72-
<View className="">
73-
<Pressable onPress={handlePaste} className="flex-row gap-1 items-center">
74-
<Image source={require('../../../../assets/icons/copy.png')} />
75-
<TextWithFont customStyle="font-bold text-white">Paste</TextWithFont>
76-
</Pressable>
77-
</View>
71+
<Pressable onPress={handlePaste} className="flex-row gap-1 items-center">
72+
<Image source={require('../../../../assets/icons/copy.png')} />
73+
<TextWithFont customStyle="font-bold text-white">Paste</TextWithFont>
74+
</Pressable>
7875

7976
<View className="flex-row my-5 bg-custom_border p-1 rounded-xl w-full">
8077
<Button onPress={() => navigation.goBack()} text={'Back'} customStyle={'w-1/2'} />
@@ -85,8 +82,8 @@ export default function ImportWalletScreen() {
8582
customStyle={`bg-${isAllFilled ? 'custom_accent' : 'white'} w-1/2`}
8683
/>
8784
</View>
88-
</View>
85+
</View >
8986
<StepIndicator totalSteps={5} currentStep={3} />
90-
</ScrollableWrapper>
87+
</ScrollableWrapper >
9188
);
9289
}

0 commit comments

Comments
 (0)