Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / pass / hooks / usePasswordGenerator.tsx
blobd50f30772265a1135238a3a9da2d246f881453ba
1 import { useCallback, useEffect, useState } from 'react';
3 import { usePassCore } from '@proton/pass/components/Core/PassCoreProvider';
4 import {
5     DEFAULT_MEMORABLE_PW_OPTIONS,
6     DEFAULT_RANDOM_PW_OPTIONS,
7     alphabeticChars,
8     digitChars,
9 } from '@proton/pass/lib/password/constants';
10 import { generatePassword } from '@proton/pass/lib/password/generator';
11 import type { GeneratePasswordConfig, GeneratePasswordMode } from '@proton/pass/lib/password/types';
12 import type { MaybeNull } from '@proton/pass/types';
13 import { merge } from '@proton/pass/utils/object/merge';
14 import debounce from '@proton/utils/debounce';
15 import noop from '@proton/utils/noop';
17 export enum CharType {
18     Alphabetic,
19     Digit,
20     Special,
23 /* Designers mixed the colors of different ui-${type}
24  * sub-themes for the character colors.. */
25 export const charTypeToClassName = {
26     [CharType.Alphabetic]: '',
27     [CharType.Digit]: 'ui-violet pass-password-generator--char-digit',
28     [CharType.Special]: 'ui-teal pass-password-generator--char-special',
31 export const getTypeFromChar = (char: string) => {
32     if (alphabeticChars.includes(char)) return CharType.Alphabetic;
33     if (digitChars.includes(char)) return CharType.Digit;
35     return CharType.Special;
38 export const getCharsGroupedByColor = (password: string) => {
39     if (password.length === 0) return [];
41     const [head, ...chars] = Array.from(password);
42     const startType = getTypeFromChar(head);
44     return chars
45         .reduce(
46             (state, currentChar) => {
47                 const currentElement = state[state.length - 1];
48                 const previousType = currentElement.color;
49                 const currentType = getTypeFromChar(currentChar);
51                 return previousType !== currentType
52                     ? [...state, { color: currentType, content: currentChar }]
53                     : [...state.slice(0, -1), { color: previousType, content: currentElement.content + currentChar }];
54             },
55             [{ color: startType, content: head }]
56         )
57         .map(({ color, content }, index) => (
58             <span className={charTypeToClassName[color]} key={index}>
59                 {content}
60             </span>
61         ));
64 type UsePasswordGeneratorOptions = {
65     initial: MaybeNull<GeneratePasswordConfig>;
66     onConfigChange: (options: GeneratePasswordConfig) => void;
69 export const usePasswordGenerator = ({ initial, onConfigChange }: UsePasswordGeneratorOptions) => {
70     const { core } = usePassCore();
71     const [config, setConfig] = useState<GeneratePasswordConfig>(initial ?? DEFAULT_MEMORABLE_PW_OPTIONS);
72     const generator = useCallback(generatePassword(core), [core]);
73     const [password, setPassword] = useState('');
75     const regeneratePassword = useCallback(() => {
76         generator(config).then(setPassword).catch(noop);
77     }, [generator, config]);
79     useEffect(regeneratePassword, []);
81     /** debounce the pw options dispatch in order to avoid swarming the
82      * store with updates when using the length slider */
83     const savePasswordOptions = useCallback(debounce(onConfigChange, 250), []);
85     const setPasswordOptions = <T extends GeneratePasswordConfig['type']>(
86         type: T,
87         update?: Partial<Extract<GeneratePasswordConfig, { type: T }>['options']>
88     ) => {
89         setConfig((options) => {
90             const newOptions = (() => {
91                 if (update) return merge(options, { options: update });
92                 if (type === 'memorable') return DEFAULT_MEMORABLE_PW_OPTIONS;
93                 if (type === 'random') return DEFAULT_RANDOM_PW_OPTIONS;
94                 return options;
95             })();
97             savePasswordOptions(newOptions);
98             return newOptions;
99         });
100     };
102     /* regenerate the password on each options change */
103     useEffect(() => regeneratePassword(), Object.values(config.options));
105     return {
106         config,
107         password,
108         setPassword,
109         setPasswordOptions,
110         regeneratePassword,
111     };
114 export type UsePasswordGeneratorResult<T extends GeneratePasswordMode = GeneratePasswordMode> = Omit<
115     ReturnType<typeof usePasswordGenerator>,
116     'config'
117 > & { config: GeneratePasswordConfig<T> };
119 export const isUsingRandomPassword = (
120     result: UsePasswordGeneratorResult
121 ): result is UsePasswordGeneratorResult<'random'> => result.config.type === 'random';
123 export const isUsingMemorablePassword = (
124     result: UsePasswordGeneratorResult
125 ): result is UsePasswordGeneratorResult<'memorable'> => result.config.type === 'memorable';