diff --git a/android/app/build.gradle b/android/app/build.gradle index 31c7158397..1d8925248a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -93,7 +93,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.55.0" + versionName "4.57.0" vectorDrawables.useSupportLibrary = true if (!isFoss) { manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] diff --git a/android/app/src/main/assets/fonts/custom.ttf b/android/app/src/main/assets/fonts/custom.ttf index 0a3f5acaf2..68f92d398a 100644 Binary files a/android/app/src/main/assets/fonts/custom.ttf and b/android/app/src/main/assets/fonts/custom.ttf differ diff --git a/app/containers/Button/index.tsx b/app/containers/Button/index.tsx index 61be086558..b9e6bf0658 100644 --- a/app/containers/Button/index.tsx +++ b/app/containers/Button/index.tsx @@ -38,6 +38,11 @@ const styles = StyleSheet.create({ ...sharedStyles.textMedium, ...sharedStyles.textAlignCenter }, + smallText: { + ...sharedStyles.textBold, + fontSize: 12, + lineHeight: 18 + }, disabled: { opacity: 0.3 } @@ -75,7 +80,11 @@ const Button: React.FC = ({ style ]; - const textStyle = [styles.text, { color: isDisabled ? colors.buttonPrimaryDisabled : resolvedTextColor, fontSize }, styleText]; + const textStyle = [ + { color: isDisabled ? colors.buttonPrimaryDisabled : resolvedTextColor, fontSize }, + small ? styles.smallText : styles.text, + styleText + ]; return ( {label ? ( @@ -117,7 +117,7 @@ export const FormTextInput = ({ (secureTextEntry || iconRight || showClearInput) && styles.inputIconRight, { backgroundColor: colors.surfaceRoom, - borderColor: colors.strokeLight, + borderColor: colors.strokeMedium, color: colors.fontTitlesLabels }, error?.error && { diff --git a/app/containers/TwoFactor/index.tsx b/app/containers/TwoFactor/index.tsx index 376f1fc98a..785df41849 100644 --- a/app/containers/TwoFactor/index.tsx +++ b/app/containers/TwoFactor/index.tsx @@ -4,18 +4,20 @@ import isEmpty from 'lodash/isEmpty'; import { sha256 } from 'js-sha256'; import Modal from 'react-native-modal'; import useDeepCompareEffect from 'use-deep-compare-effect'; -import { connect } from 'react-redux'; import { FormTextInput } from '../TextInput'; import I18n from '../../i18n'; import EventEmitter from '../../lib/methods/helpers/events'; import { useTheme } from '../../theme'; -import { themes } from '../../lib/constants'; import Button from '../Button'; import sharedStyles from '../../views/Styles'; import styles from './styles'; -import { IApplicationState } from '../../definitions'; +import { ICredentials } from '../../definitions'; import { Services } from '../../lib/services'; +import { useAppSelector } from '../../lib/hooks'; +import Toast from '../Toast'; +import { showToast } from '../../lib/methods/helpers/showToast'; +import log from '../../lib/methods/helpers/log'; export const TWO_FACTOR = 'TWO_FACTOR'; @@ -32,6 +34,7 @@ interface IMethods { } interface EventListenerMethod { + params?: ICredentials; method?: keyof IMethods; submit?: (param: string) => void; cancel?: () => void; @@ -55,15 +58,31 @@ const methods: IMethods = { } }; -const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) => { - const { theme } = useTheme(); +const TwoFactor = React.memo(() => { + const { colors } = useTheme(); + const { isMasterDetail } = useAppSelector(state => ({ + isMasterDetail: state.app.isMasterDetail as boolean + })); const [visible, setVisible] = useState(false); const [data, setData] = useState({}); const [code, setCode] = useState(''); const method = data.method ? methods[data.method] : null; const isEmail = data.method === 'email'; - const sendEmail = () => Services.sendEmailCode(); + const params = data?.params; + + const sendEmail = async () => { + try { + if (params?.user) { + const response = await Services.sendEmailCode(params?.user); + if (response.success) { + showToast(I18n.t('Two_Factor_Success_message')); + } + } + } catch (e) { + log(e); + } + }; useDeepCompareEffect(() => { if (!isEmpty(data)) { @@ -74,7 +93,9 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) = } }, [data]); - const showTwoFactor = (args: EventListenerMethod) => setData(args); + const showTwoFactor = (args: EventListenerMethod) => { + setData(args); + }; useEffect(() => { const listener = EventEmitter.addEventListener(TWO_FACTOR, showTwoFactor); @@ -102,47 +123,58 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) = setData({}); }; - const color = themes[theme].fontTitlesLabels; + const color = colors.fontTitlesLabels; return ( - + } + avoidKeyboard + useNativeDriver + isVisible={visible} + hideModalContentWhileAnimating> {I18n.t(method?.title || 'Two_Factor_Authentication')} {method?.text ? {I18n.t(method.text)} : null} InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} returnKeyType='send' autoCapitalize='none' + testID='two-factor-input' + accessibilityLabel={I18n.t( + data?.method === 'password' ? 'Label_Input_Two_Factor_Password' : 'Label_Input_Two_Factor_Code' + )} + value={code} + inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} onChangeText={setCode} onSubmitEditing={onSubmit} keyboardType={method?.keyboardType} secureTextEntry={method?.secureTextEntry} error={data.invalid ? { error: 'totp-invalid', reason: I18n.t('Code_or_password_invalid') } : undefined} - testID='two-factor-input' + containerStyle={styles.containerInput} /> + {isEmail ? ( - - {I18n.t('Resend_email')} - +