Skip to content

Commit

Permalink
Merge branch 'develop' into chore-a11y-delete-account-actionsheet
Browse files Browse the repository at this point in the history
  • Loading branch information
OtavioStasiak authored Jan 8, 2025
2 parents 0806983 + b938b0d commit 6e0393b
Show file tree
Hide file tree
Showing 81 changed files with 1,319 additions and 673 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer
versionName "4.56.0"
versionName "4.57.0"
vectorDrawables.useSupportLibrary = true
if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
Expand Down
11 changes: 10 additions & 1 deletion app/containers/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const styles = StyleSheet.create({
...sharedStyles.textMedium,
...sharedStyles.textAlignCenter
},
smallText: {
...sharedStyles.textBold,
fontSize: 12,
lineHeight: 18
},
disabled: {
opacity: 0.3
}
Expand Down Expand Up @@ -75,7 +80,11 @@ const Button: React.FC<IButtonProps> = ({
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 (
<Touchable
Expand Down
1 change: 1 addition & 0 deletions app/containers/List/ListInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const styles = StyleSheet.create({
},
text: {
fontSize: 14,
lineHeight: 20,
...sharedStyles.textRegular
}
});
Expand Down
4 changes: 2 additions & 2 deletions app/containers/TextInput/FormTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 16,
paddingVertical: 10,
borderWidth: 1,
borderRadius: 2
borderRadius: 4
},
inputIconLeft: {
paddingLeft: 45
Expand Down Expand Up @@ -118,7 +118,7 @@ export const FormTextInput = ({
(secureTextEntry || iconRight || showClearInput) && styles.inputIconRight,
{
backgroundColor: colors.surfaceRoom,
borderColor: colors.strokeLight,
borderColor: colors.strokeMedium,
color: colors.fontTitlesLabels
},
error?.error && {
Expand Down
74 changes: 53 additions & 21 deletions app/containers/TwoFactor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -32,6 +34,7 @@ interface IMethods {
}

interface EventListenerMethod {
params?: ICredentials;
method?: keyof IMethods;
submit?: (param: string) => void;
cancel?: () => void;
Expand All @@ -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<EventListenerMethod>({});
const [code, setCode] = useState<string>('');

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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -102,47 +123,58 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) =
setData({});
};

const color = themes[theme].fontTitlesLabels;
const color = colors.fontTitlesLabels;
return (
<Modal avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
<Modal
customBackdrop={<View aria-hidden style={[styles.overlay, { backgroundColor: colors.overlayBackground }]} />}
avoidKeyboard
useNativeDriver
isVisible={visible}
hideModalContentWhileAnimating>
<View style={styles.container} testID='two-factor'>
<View
style={[
styles.content,
isMasterDetail && [sharedStyles.modalFormSheet, styles.tablet],
{ backgroundColor: themes[theme].surfaceTint }
{ backgroundColor: colors.surfaceTint }
]}>
<Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text>
{method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null}
<FormTextInput
value={code}
inputRef={(e: any) => 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 ? (
<Text style={[styles.sendEmail, { color }]} onPress={sendEmail}>
{I18n.t('Resend_email')}
</Text>
<Button
small
title={I18n.t('Resend_email')}
style={[styles.button, { marginTop: 12 }]}
type='secondary'
onPress={sendEmail}
/>
) : null}
<View style={styles.buttonContainer}>
<Button title={I18n.t('Cancel')} type='secondary' style={styles.button} onPress={onCancel} />
<Button title={I18n.t('Verify')} type='primary' style={styles.button} onPress={onSubmit} testID='two-factor-send' />
</View>
</View>
<Toast />
</View>
</Modal>
);
});

const mapStateToProps = (state: IApplicationState) => ({
isMasterDetail: state.app.isMasterDetail
});

export default connect(mapStateToProps)(TwoFactor);
export default TwoFactor;
18 changes: 14 additions & 4 deletions app/containers/TwoFactor/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
padding: 16
},
content: {
padding: 16,
Expand All @@ -15,13 +15,14 @@ export default StyleSheet.create({
},
title: {
fontSize: 16,
paddingBottom: 8,
lineHeight: 24,
marginBottom: 12,
...sharedStyles.textBold,
...sharedStyles.textAlignCenter
},
subtitle: {
fontSize: 14,
paddingBottom: 8,
marginBottom: 12,
...sharedStyles.textRegular,
...sharedStyles.textAlignCenter
},
Expand All @@ -37,9 +38,18 @@ export default StyleSheet.create({
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between'
justifyContent: 'space-between',
marginTop: 36
},
tablet: {
height: undefined
},
overlay: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
containerInput: {
marginBottom: 0
}
});
8 changes: 5 additions & 3 deletions app/containers/message/Urls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Clipboard from '@react-native-clipboard/clipboard';
import FastImage from 'react-native-fast-image';
import { dequal } from 'dequal';

import { useAppSelector } from '../../lib/hooks';
import Touchable from './Touchable';
import openLink from '../../lib/methods/helpers/openLink';
import sharedStyles from '../../views/Styles';
Expand Down Expand Up @@ -96,8 +97,8 @@ const UrlImage = ({ image, hasContent }: { image: string; hasContent: boolean })
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
...(imageDimensions.width <= 64 && { width: 64 }),
...(imageDimensions.height <= 64 && { height: 64 })
...imageDimensions.width <= 64 && { width: 64 },
...imageDimensions.height <= 64 && { height: 64 }
};
if (!hasContent) {
containerStyle = {
Expand Down Expand Up @@ -128,6 +129,7 @@ type TImageLoadedState = 'loading' | 'done' | 'error';
const Url = ({ url }: { url: IUrl }) => {
const { colors, theme } = useTheme();
const { baseUrl, user } = useContext(MessageContext);
const API_Embed = useAppSelector(state => state.settings.API_Embed);
const getImageUrl = () => {
const imageUrl = url.image || url.url;

Expand All @@ -146,7 +148,7 @@ const Url = ({ url }: { url: IUrl }) => {

const hasContent = !!(url.title || url.description);

if (!url || url?.ignoreParse) {
if (!url || url?.ignoreParse || !API_Embed) {
return null;
}

Expand Down
4 changes: 4 additions & 0 deletions app/definitions/IRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface IRequestTranscript {
subject: string;
}

export type TUserWaitingForE2EKeys = { userId: string; ts: Date };

export interface IRoom {
fname?: string;
_id: string;
Expand All @@ -34,6 +36,7 @@ export interface IRoom {
livechatData?: any;
tags?: string[];
e2eKeyId?: string;
usersWaitingForE2EKeys?: TUserWaitingForE2EKeys[];
avatarETag?: string;
latest?: string;
default?: boolean;
Expand Down Expand Up @@ -217,6 +220,7 @@ export interface IServerRoom extends IRocketChatRecord {
reactWhenReadOnly?: boolean;
joinCodeRequired?: boolean;
e2eKeyId?: string;
usersWaitingForE2EKeys?: TUserWaitingForE2EKeys[];
v?: {
_id?: string;
token?: string;
Expand Down
8 changes: 7 additions & 1 deletion app/definitions/ISubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Relation from '@nozbe/watermelondb/Relation';

import { ILastMessage, TMessageModel } from './IMessage';
import { IRocketChatRecord } from './IRocketChatRecord';
import { IOmnichannelSource, RoomID, RoomType } from './IRoom';
import { IOmnichannelSource, RoomID, RoomType, TUserWaitingForE2EKeys } from './IRoom';
import { IServedBy } from './IServedBy';
import { TThreadModel } from './IThread';
import { TThreadMessageModel } from './IThreadMessage';
Expand Down Expand Up @@ -35,6 +35,8 @@ export enum ERoomTypes {

type RelationModified<T extends Model> = { fetch(): Promise<T[]> } & Relation<T>;

type OldKey = { e2eKeyId: string; ts: Date; E2EKey: string };

export interface ISubscription {
_id: string;
id: string;
Expand Down Expand Up @@ -93,9 +95,11 @@ export interface ISubscription {
livechatData?: any;
tags?: string[];
E2EKey?: string;
oldRoomKeys?: OldKey[];
E2ESuggestedKey?: string | null;
encrypted?: boolean;
e2eKeyId?: string;
usersWaitingForE2EKeys?: TUserWaitingForE2EKeys[];
avatarETag?: string;
teamId?: string;
teamMain?: boolean;
Expand Down Expand Up @@ -153,7 +157,9 @@ export interface IServerSubscription extends IRocketChatRecord {
onHold?: boolean;
encrypted?: boolean;
E2EKey?: string;
oldRoomKeys?: OldKey[];
E2ESuggestedKey?: string | null;
usersWaitingForE2EKeys?: TUserWaitingForE2EKeys[];
unreadAlert?: 'default' | 'all' | 'mentions' | 'nothing';

fname?: unknown;
Expand Down
11 changes: 11 additions & 0 deletions app/definitions/rest/v1/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,21 @@ export type E2eEndpoints = {
'e2e.rejectSuggestedGroupKey': {
POST: (params: { rid: string }) => {};
};
'e2e.fetchUsersWaitingForGroupKey': {
GET: (params: { roomIds: string[] }) => {
usersWaitingForE2EKeys: any;
};
};
'e2e.provideUsersSuggestedGroupKeys': {
POST: (params: { usersSuggestedGroupKeys: any }) => void;
};
'e2e.setRoomKeyID': {
POST: (params: { rid: string; keyID: string }) => {};
};
'e2e.fetchMyKeys': {
GET: () => { public_key: string; private_key: string };
};
'e2e.resetRoomKey': {
POST: (params: { rid: string; e2eKey: string; e2eKeyId: string }) => void;
};
};
Loading

0 comments on commit 6e0393b

Please sign in to comment.