Skip to content

Commit 916fae1

Browse files
committed
Refactor RandomPassword screen to improve password generation and UI
1 parent 2e843f8 commit 916fae1

File tree

1 file changed

+157
-13
lines changed

1 file changed

+157
-13
lines changed

src/screens/Try/RandomPassword.tsx

Lines changed: 157 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,175 @@
1+
import Btn from '@components/Button'
12
import { Gap12 } from '@components/Gap'
23
import Range from '@components/Range'
3-
import { SettGroup, SettText, SettWrapper } from '@components/Settings'
4+
import { Check, SettGroup, SettOption, SettText, SettWrapper } from '@components/Settings'
45
import { Txt } from '@components/Text'
6+
import Clipboard from '@react-native-clipboard/clipboard'
7+
import { Medium } from '@utils/fonts'
58
import type { NavProp } from '@utils/types'
6-
import React, { useState } from 'react'
7-
import { View } from 'react-native'
9+
import React, { memo, useCallback, useEffect, useState } from 'react'
10+
import { ToastAndroid, TouchableOpacity } from 'react-native'
811

9-
const MIN = 1
10-
const MAX = 49
11-
function getCurrVal(len: number) {
12-
return Math.floor(len * MAX + MIN)
13-
}
12+
const MAX = 50
13+
const MIN = 4
14+
const INITIAL = 20
1415

1516
export default function RandomPassword({ navigation }: NavProp) {
16-
const [len, setLen] = useState<number>(10 / 50)
17+
const [len, setLen] = useState<number>(INITIAL)
18+
19+
const [generatedPassword, setGeneratedPassword] = useState('')
20+
21+
const [lowercase, setLo] = useState(true)
22+
const [uppercase, setUp] = useState(true)
23+
const [numerical, setNum] = useState(true)
24+
const [special, setSp] = useState(true)
25+
26+
function generatePassword() {
27+
console.log(len)
28+
setGeneratedPassword(
29+
randomPass({
30+
len,
31+
lowercase,
32+
uppercase,
33+
numerical,
34+
special,
35+
}),
36+
)
37+
}
1738

39+
useEffect(() => {
40+
setGeneratedPassword(
41+
randomPass({
42+
len,
43+
lowercase,
44+
uppercase,
45+
numerical,
46+
special,
47+
}),
48+
)
49+
}, [len, lowercase, numerical, special, uppercase])
1850
return (
1951
<SettWrapper navigation={navigation} title='Random Password'>
2052
<Gap12>
2153
<SettText className='mt-3'>Here you will set some options to generate a random password</SettText>
2254
<SettGroup title='Password Length'>
23-
<View>
24-
<Range value={len} setValue={setLen} Left={<Txt>1</Txt>} Right={<Txt>50</Txt>} />
25-
<Txt className='pb-2 text-center'>Length {getCurrVal(len)}</Txt>
26-
</View>
55+
<MemoRange setVal={setLen} initial={INITIAL} max={MAX} min={MIN} Bottom={BottomPart} />
56+
</SettGroup>
57+
58+
<SettGroup title='Password Contains'>
59+
<SettOption
60+
onPress={() => setLo(!lowercase)}
61+
title='Lowercase Characters'
62+
Icon={<Check checked={lowercase} />}
63+
/>
64+
<SettOption
65+
onPress={() => setUp(!uppercase)}
66+
title='Uppercase Characters'
67+
Icon={<Check checked={uppercase} />}
68+
/>
69+
<SettOption
70+
onPress={() => setNum(!numerical)}
71+
title='Numerical Characters'
72+
Icon={<Check checked={numerical} />}
73+
/>
74+
<SettOption onPress={() => setSp(!special)} title='Special Characters' Icon={<Check checked={special} />} />
75+
</SettGroup>
76+
<SettGroup>
77+
{generatedPassword ? (
78+
<TouchableOpacity
79+
activeOpacity={0.7}
80+
onPress={() => {
81+
Clipboard.setString(generatedPassword)
82+
ToastAndroid.show('Password Copied', ToastAndroid.SHORT)
83+
}}
84+
>
85+
<Medium className='px-5 py-3 text-xs text-accent' numberOfLines={1}>
86+
{generatedPassword}
87+
</Medium>
88+
</TouchableOpacity>
89+
) : (
90+
<Medium className='px-5 py-3 text-xs text-red-500'>Please select at least one of the above options.</Medium>
91+
)}
92+
</SettGroup>
93+
<SettText className='text-center'>Tap to copy the password</SettText>
94+
95+
<SettGroup className='px-5 py-5'>
96+
<Btn title='Generate Password' onPress={generatePassword} />
2797
</SettGroup>
2898
</Gap12>
2999
</SettWrapper>
30100
)
31101
}
102+
103+
const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz'
104+
const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
105+
const NUMERICAL = '0123456789'
106+
const SPECIAL = '~@#$%^&*()_+{}":><?/*'
107+
108+
type randomPassParams = {
109+
len: number
110+
lowercase: boolean
111+
uppercase: boolean
112+
numerical: boolean
113+
special: boolean
114+
}
115+
116+
function randomPass({ len, lowercase, uppercase, numerical, special }: randomPassParams) {
117+
const pass = []
118+
if (lowercase) pass.push(LOWERCASE[Math.floor(Math.random() * LOWERCASE.length)])
119+
if (uppercase) pass.push(UPPERCASE[Math.floor(Math.random() * UPPERCASE.length)])
120+
if (numerical) pass.push(NUMERICAL[Math.floor(Math.random() * NUMERICAL.length)])
121+
if (special) pass.push(SPECIAL[Math.floor(Math.random() * SPECIAL.length)])
122+
123+
const remaining = len + -lowercase + -uppercase + -numerical + -special
124+
125+
const chars =
126+
(lowercase ? LOWERCASE : '') +
127+
(uppercase ? UPPERCASE : '') +
128+
(numerical ? NUMERICAL : '') +
129+
(special ? SPECIAL : '')
130+
131+
for (let i = 0; i < remaining; i++) {
132+
pass.push(chars[Math.floor(Math.random() * chars.length)])
133+
}
134+
135+
pass.sort(() => Math.random() - 0.5)
136+
137+
return pass.join('')
138+
}
139+
140+
type MemoRangeTypes = {
141+
setVal: (x: number) => void
142+
initial: number
143+
max: number
144+
min: number
145+
Left?: React.ReactNode
146+
Right?: React.ReactNode
147+
Bottom?: ({ val }: { val: number }) => React.ReactNode
148+
}
149+
150+
const MemoRange = memo(({ setVal, initial, max, min, Left, Right, Bottom }: MemoRangeTypes) => {
151+
const [len, setLen] = useState<number>((initial - min) / (max - min))
152+
153+
const getCurrVal = useCallback((x: number) => Math.floor(min + x * (max - min)), [max, min])
154+
155+
useEffect(() => {
156+
const timer = setTimeout(() => setVal(getCurrVal(len)), 50)
157+
return () => clearTimeout(timer)
158+
}, [getCurrVal, len, setVal])
159+
160+
return (
161+
<>
162+
<Range
163+
value={len}
164+
setValue={setLen}
165+
Left={Left === undefined ? <Txt>{min}</Txt> : Left}
166+
Right={Right === undefined ? <Txt>{max}</Txt> : Right}
167+
/>
168+
{Bottom && <Bottom val={getCurrVal(len)} />}
169+
</>
170+
)
171+
})
172+
173+
function BottomPart({ val }: { val: number }) {
174+
return <Txt className='pb-2 text-center'>Password Length {val}</Txt>
175+
}

0 commit comments

Comments
 (0)