Merge 4.23.0 into single-server (#3575)

This commit is contained in:
Diego Mello 2021-12-24 10:12:49 -03:00 committed by GitHub
parent b564eddcfd
commit 2b8757571c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
158 changed files with 2557 additions and 1525 deletions

View File

@ -1419,6 +1419,7 @@ Array [
</Text> </Text>
</View> </View>
<View <View
accessibilityLabel="Use"
accessible={true} accessible={true}
focusable={true} focusable={true}
onClick={[Function]} onClick={[Function]}
@ -1581,6 +1582,7 @@ Array [
</Text> </Text>
</View> </View>
<View <View
accessibilityLabel="Use"
accessible={true} accessible={true}
focusable={true} focusable={true}
onClick={[Function]} onClick={[Function]}
@ -41244,6 +41246,7 @@ exports[`Storyshots Message Show a button as attachment 1`] = `
Test Button Test Button
</Text> </Text>
<View <View
accessibilityLabel="Text button"
accessible={true} accessible={true}
focusable={true} focusable={true}
onClick={[Function]} onClick={[Function]}

View File

@ -144,13 +144,15 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer versionCode VERSIONCODE as Integer
versionName "4.22.0" versionName "4.23.0"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
if (!isFoss) { if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below! missingDimensionStrategy "RNNotifications.reactNativeVersion", "reactNative60" // See note below!
} }
resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative" resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative"
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
} }
signingConfigs { signingConfigs {
@ -203,6 +205,10 @@ android {
dimension = "app" dimension = "app"
buildConfigField "boolean", "IS_OFFICIAL", "false" buildConfigField "boolean", "IS_OFFICIAL", "false"
} }
e2e {
dimension = "app"
buildConfigField "boolean", "IS_OFFICIAL", "false"
}
foss { foss {
dimension = "type" dimension = "type"
buildConfigField "boolean", "FDROID_BUILD", "true" buildConfigField "boolean", "FDROID_BUILD", "true"
@ -230,6 +236,16 @@ android {
java.srcDirs = ['src/main/java', 'src/play/java'] java.srcDirs = ['src/main/java', 'src/play/java']
manifest.srcFile 'src/play/AndroidManifest.xml' manifest.srcFile 'src/play/AndroidManifest.xml'
} }
e2ePlayDebug {
java.srcDirs = ['src/main/java', 'src/play/java']
res.srcDirs = ['src/experimental/res']
manifest.srcFile 'src/play/AndroidManifest.xml'
}
e2ePlayRelease {
java.srcDirs = ['src/main/java', 'src/play/java']
res.srcDirs = ['src/experimental/res']
manifest.srcFile 'src/play/AndroidManifest.xml'
}
} }
applicationVariants.all { variant -> applicationVariants.all { variant ->
@ -294,6 +310,8 @@ dependencies {
implementation "com.tencent:mmkv-static:1.2.1" implementation "com.tencent:mmkv-static:1.2.1"
implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0" implementation "com.squareup.okhttp3:okhttp-urlconnection:4.9.0"
androidTestImplementation('com.wix:detox:+') { transitive = true }
androidTestImplementation 'junit:junit:4.12'
} }
// Run this once to be able to run the application with BUCK // Run this once to be able to run the application with BUCK

View File

@ -0,0 +1,32 @@
// Replace "com.example" here and below with your app's package name from the top of MainActivity.java
package chat.rocket.reactnative;
import com.wix.detox.Detox;
import com.wix.detox.config.DetoxConfig;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class DetoxTest {
@Rule
// Replace 'MainActivity' with the value of android:name entry in
// <activity> in AndroidManifest.xml
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
@Test
public void runDetoxTests() {
DetoxConfig detoxConfig = new DetoxConfig();
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
detoxConfig.rnContextLoadTimeoutSec = (chat.rocket.reactnative.BuildConfig.DEBUG ? 180 : 60);
Detox.runTests(mActivityRule, detoxConfig);
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:tools="http://schemas.android.com/tools">
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user"
tools:ignore="AcceptsUserCertificates" />
</trust-anchors>
</base-config>
</network-security-config>

View File

@ -53,6 +53,10 @@ allprojects {
url("$rootDir/../node_modules/jsc-android/dist") url("$rootDir/../node_modules/jsc-android/dist")
} }
maven {
url "$rootDir/../node_modules/detox/Detox-android"
}
maven { maven {
url jitsi_url url jitsi_url
} }

View File

@ -3,6 +3,7 @@ import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SetUsernameStackParamList, StackParamList } from './navigationTypes';
import Navigation from './lib/Navigation'; import Navigation from './lib/Navigation';
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation'; import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app'; import { ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE, ROOT_SET_USERNAME } from './actions/app';
@ -17,7 +18,7 @@ import { ThemeContext } from './theme';
import { setCurrentScreen } from './utils/log'; import { setCurrentScreen } from './utils/log';
// SetUsernameStack // SetUsernameStack
const SetUsername = createStackNavigator(); const SetUsername = createStackNavigator<SetUsernameStackParamList>();
const SetUsernameStack = () => ( const SetUsernameStack = () => (
<SetUsername.Navigator screenOptions={defaultHeader}> <SetUsername.Navigator screenOptions={defaultHeader}>
<SetUsername.Screen name='SetUsernameView' component={SetUsernameView} /> <SetUsername.Screen name='SetUsernameView' component={SetUsernameView} />
@ -25,7 +26,7 @@ const SetUsernameStack = () => (
); );
// App // App
const Stack = createStackNavigator(); const Stack = createStackNavigator<StackParamList>();
const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => { const App = React.memo(({ root, isMasterDetail }: { root: string; isMasterDetail: boolean }) => {
if (!root) { if (!root) {
return null; return null;

View File

@ -124,7 +124,11 @@ const ActionSheet = React.memo(
const renderFooter = () => const renderFooter = () =>
data?.hasCancel ? ( data?.hasCancel ? (
<Button onPress={hide} style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]} theme={theme}> <Button
onPress={hide}
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
theme={theme}
accessibilityLabel={I18n.t('Cancel')}>
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text> <Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
</Button> </Button>
) : null; ) : null;

View File

@ -17,7 +17,7 @@ export const useActionSheet = () => useContext(context);
const { Provider, Consumer } = context; const { Provider, Consumer } = context;
export const withActionSheet = <P extends object>(Component: React.ComponentType<P>) => export const withActionSheet = (Component: any): any =>
forwardRef((props: any, ref: ForwardedRef<any>) => ( forwardRef((props: any, ref: ForwardedRef<any>) => (
<Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer> <Consumer>{(contexts: any) => <Component {...props} {...contexts} ref={ref} />}</Consumer>
)); ));

View File

@ -70,6 +70,7 @@ export default class Button extends React.PureComponent<Partial<IButtonProps>, a
disabled && styles.disabled, disabled && styles.disabled,
style style
]} ]}
accessibilityLabel={title}
{...otherProps}> {...otherProps}>
{loading ? ( {loading ? (
<ActivityIndicator color={textColor} /> <ActivityIndicator color={textColor} />

View File

@ -31,7 +31,7 @@ interface IEmojiPickerProps {
customEmojis?: any; customEmojis?: any;
style: object; style: object;
theme?: string; theme?: string;
onEmojiSelected?: Function; onEmojiSelected?: ((emoji: any) => void) | ((keyboardId: any, params?: any) => void);
tabEmojiStyle?: object; tabEmojiStyle?: object;
} }
@ -201,4 +201,5 @@ const mapStateToProps = (state: any) => ({
customEmojis: state.customEmojis customEmojis: state.customEmojis
}); });
export default connect(mapStateToProps)(withTheme(EmojiPicker)); // TODO - remove this as any, at the new PR to fix the HOC erros
export default connect(mapStateToProps)(withTheme(EmojiPicker)) as any;

View File

@ -2,7 +2,7 @@ import React from 'react';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
interface IHeaderButtonContainer { interface IHeaderButtonContainer {
children: JSX.Element; children: React.ReactNode;
left?: boolean; left?: boolean;
} }

View File

@ -423,4 +423,4 @@ const mapStateToProps = (state: any) => ({
services: state.login.services services: state.login.services
}); });
export default connect(mapStateToProps)(withTheme(LoginServices)); export default connect(mapStateToProps)(withTheme(LoginServices)) as any;

View File

@ -13,7 +13,7 @@ interface IMessageBoxEmojiKeyboard {
} }
export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> { export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiKeyboard, any> {
private readonly baseUrl: any; private readonly baseUrl: string;
constructor(props: IMessageBoxEmojiKeyboard) { constructor(props: IMessageBoxEmojiKeyboard) {
super(props); super(props);

View File

@ -13,6 +13,7 @@ import { events, logEvent } from '../../utils/log';
interface IMessageBoxRecordAudioProps { interface IMessageBoxRecordAudioProps {
theme: string; theme: string;
permissionToUpload: boolean;
recordingCallback: Function; recordingCallback: Function;
onFinish: Function; onFinish: Function;
} }
@ -192,9 +193,11 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
}; };
render() { render() {
const { theme } = this.props; const { theme, permissionToUpload } = this.props;
const { isRecording, isRecorderActive } = this.state; const { isRecording, isRecorderActive } = this.state;
if (!permissionToUpload) {
return null;
}
if (!isRecording && !isRecorderActive) { if (!isRecording && !isRecorderActive) {
return ( return (
<BorderlessButton <BorderlessButton

View File

@ -109,6 +109,8 @@ interface IMessageBoxProps {
sharing: boolean; sharing: boolean;
isActionsEnabled: boolean; isActionsEnabled: boolean;
usedCannedResponse: string; usedCannedResponse: string;
uploadFilePermission: string[];
serverVersion: string;
} }
interface IMessageBoxState { interface IMessageBoxState {
@ -124,6 +126,7 @@ interface IMessageBoxState {
}; };
tshow: boolean; tshow: boolean;
mentionLoading: boolean; mentionLoading: boolean;
permissionToUpload: boolean;
} }
class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> { class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
@ -179,41 +182,13 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
showCommandPreview: false, showCommandPreview: false,
command: {}, command: {},
tshow: false, tshow: false,
mentionLoading: false mentionLoading: false,
permissionToUpload: true
}; };
this.text = ''; this.text = '';
this.selection = { start: 0, end: 0 }; this.selection = { start: 0, end: 0 };
this.focused = false; this.focused = false;
// MessageBox Actions
this.options = [
{
title: I18n.t('Take_a_photo'),
icon: 'camera-photo',
onPress: this.takePhoto
},
{
title: I18n.t('Take_a_video'),
icon: 'camera',
onPress: this.takeVideo
},
{
title: I18n.t('Choose_from_library'),
icon: 'image',
onPress: this.chooseFromLibrary
},
{
title: I18n.t('Choose_file'),
icon: 'attach',
onPress: this.chooseFile
},
{
title: I18n.t('Create_Discussion'),
icon: 'discussions',
onPress: this.createDiscussion
}
];
const libPickerLabels = { const libPickerLabels = {
cropperChooseText: I18n.t('Choose'), cropperChooseText: I18n.t('Choose'),
cropperCancelText: I18n.t('Cancel'), cropperCancelText: I18n.t('Cancel'),
@ -277,6 +252,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
this.onChangeText(usedCannedResponse); this.onChangeText(usedCannedResponse);
} }
this.setOptions();
this.unsubscribeFocus = navigation.addListener('focus', () => { this.unsubscribeFocus = navigation.addListener('focus', () => {
// didFocus // didFocus
// We should wait pushed views be dismissed // We should wait pushed views be dismissed
@ -321,10 +298,20 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
} }
} }
shouldComponentUpdate(nextProps: any, nextState: any) { shouldComponentUpdate(nextProps: IMessageBoxProps, nextState: IMessageBoxState) {
const { showEmojiKeyboard, showSend, recording, mentions, commandPreview, tshow, mentionLoading, trackingType } = this.state; const {
showEmojiKeyboard,
showSend,
recording,
mentions,
commandPreview,
tshow,
mentionLoading,
trackingType,
permissionToUpload
} = this.state;
const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse } = this.props; const { roomType, replying, editing, isFocused, message, theme, usedCannedResponse, uploadFilePermission } = this.props;
if (nextProps.theme !== theme) { if (nextProps.theme !== theme) {
return true; return true;
} }
@ -358,6 +345,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
if (nextState.tshow !== tshow) { if (nextState.tshow !== tshow) {
return true; return true;
} }
if (nextState.permissionToUpload !== permissionToUpload) {
return true;
}
if (!dequal(nextState.mentions, mentions)) { if (!dequal(nextState.mentions, mentions)) {
return true; return true;
} }
@ -367,12 +357,22 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
if (!dequal(nextProps.message?.id, message?.id)) { if (!dequal(nextProps.message?.id, message?.id)) {
return true; return true;
} }
if (!dequal(nextProps.uploadFilePermission, uploadFilePermission)) {
return true;
}
if (nextProps.usedCannedResponse !== usedCannedResponse) { if (nextProps.usedCannedResponse !== usedCannedResponse) {
return true; return true;
} }
return false; return false;
} }
componentDidUpdate(prevProps: IMessageBoxProps) {
const { uploadFilePermission } = this.props;
if (!dequal(prevProps.uploadFilePermission, uploadFilePermission)) {
this.setOptions();
}
}
componentWillUnmount() { componentWillUnmount() {
console.countReset(`${this.constructor.name}.render calls`); console.countReset(`${this.constructor.name}.render calls`);
if (this.onChangeText && this.onChangeText.stop) { if (this.onChangeText && this.onChangeText.stop) {
@ -404,6 +404,19 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
} }
} }
setOptions = async () => {
const { uploadFilePermission, rid } = this.props;
// Servers older than 4.2
if (!uploadFilePermission) {
this.setState({ permissionToUpload: true });
return;
}
const permissionToUpload = await RocketChat.hasPermission([uploadFilePermission], rid);
this.setState({ permissionToUpload: permissionToUpload[0] });
};
onChangeText: any = (text: string): void => { onChangeText: any = (text: string): void => {
const isTextEmpty = text.length === 0; const isTextEmpty = text.length === 0;
this.setShowSend(!isTextEmpty); this.setShowSend(!isTextEmpty);
@ -666,8 +679,9 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
}; };
canUploadFile = (file: any) => { canUploadFile = (file: any) => {
const { permissionToUpload } = this.state;
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props; const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize); const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, permissionToUpload);
if (result.success) { if (result.success) {
return true; return true;
} }
@ -766,8 +780,41 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
showMessageBoxActions = () => { showMessageBoxActions = () => {
logEvent(events.ROOM_SHOW_BOX_ACTIONS); logEvent(events.ROOM_SHOW_BOX_ACTIONS);
const { permissionToUpload } = this.state;
const { showActionSheet } = this.props; const { showActionSheet } = this.props;
showActionSheet({ options: this.options });
const options = [];
if (permissionToUpload) {
options.push(
{
title: I18n.t('Take_a_photo'),
icon: 'camera-photo',
onPress: this.takePhoto
},
{
title: I18n.t('Take_a_video'),
icon: 'camera',
onPress: this.takeVideo
},
{
title: I18n.t('Choose_from_library'),
icon: 'image',
onPress: this.chooseFromLibrary
},
{
title: I18n.t('Choose_file'),
icon: 'attach',
onPress: this.chooseFile
}
);
}
options.push({
title: I18n.t('Create_Discussion'),
icon: 'discussions',
onPress: this.createDiscussion
});
showActionSheet({ options });
}; };
editCancel = () => { editCancel = () => {
@ -968,8 +1015,17 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
}; };
renderContent = () => { renderContent = () => {
const { recording, showEmojiKeyboard, showSend, mentions, trackingType, commandPreview, showCommandPreview, mentionLoading } = const {
this.state; recording,
showEmojiKeyboard,
showSend,
mentions,
trackingType,
commandPreview,
showCommandPreview,
mentionLoading,
permissionToUpload
} = this.state;
const { const {
editing, editing,
message, message,
@ -995,7 +1051,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
const recordAudio = const recordAudio =
showSend || !Message_AudioRecorderEnabled ? null : ( showSend || !Message_AudioRecorderEnabled ? null : (
<RecordAudio theme={theme} recordingCallback={this.recordingCallback} onFinish={this.finishAudioMessage} /> <RecordAudio
theme={theme}
recordingCallback={this.recordingCallback}
onFinish={this.finishAudioMessage}
permissionToUpload={permissionToUpload}
/>
); );
const commandsPreviewAndMentions = !recording ? ( const commandsPreviewAndMentions = !recording ? (
@ -1117,11 +1178,12 @@ const mapStateToProps = (state: any) => ({
user: getUserSelector(state), user: getUserSelector(state),
FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList, FileUpload_MediaTypeWhiteList: state.settings.FileUpload_MediaTypeWhiteList,
FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize, FileUpload_MaxFileSize: state.settings.FileUpload_MaxFileSize,
Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled Message_AudioRecorderEnabled: state.settings.Message_AudioRecorderEnabled,
uploadFilePermission: state.permissions['mobile-upload-file']
}); });
const dispatchToProps = { const dispatchToProps = {
typing: (rid: any, status: any) => userTypingAction(rid, status) typing: (rid: any, status: any) => userTypingAction(rid, status)
}; };
// @ts-ignore // @ts-ignore
export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)); export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any;

View File

@ -20,7 +20,7 @@ interface IPasscodeBase {
previousPasscode?: string; previousPasscode?: string;
title: string; title: string;
subtitle?: string; subtitle?: string;
showBiometry?: string; showBiometry?: boolean;
onEndProcess: Function; onEndProcess: Function;
onError?: Function; onError?: Function;
onBiometryPress?(): void; onBiometryPress?(): void;

View File

@ -15,7 +15,7 @@ import I18n from '../../i18n';
interface IPasscodePasscodeEnter { interface IPasscodePasscodeEnter {
theme: string; theme: string;
hasBiometry: string; hasBiometry: boolean;
finishProcess: Function; finishProcess: Function;
} }

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, Text, TextInputProps, View } from 'react-native';
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
import TextInput from '../presentation/TextInput'; import TextInput from '../presentation/TextInput';
@ -45,7 +45,7 @@ const styles = StyleSheet.create({
}); });
interface ISearchBox { interface ISearchBox {
onChangeText: () => void; onChangeText: TextInputProps['onChangeText'];
onSubmitEditing: () => void; onSubmitEditing: () => void;
hasCancel: boolean; hasCancel: boolean;
onCancelPress: Function; onCancelPress: Function;

View File

@ -8,6 +8,7 @@ interface IStatus {
status: string; status: string;
size: number; size: number;
style?: StyleProp<TextStyle>; style?: StyleProp<TextStyle>;
testID?: string;
} }
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => { const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatus) => {

View File

@ -43,7 +43,11 @@ const Content = React.memo(
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>; content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
} else if (props.isEncrypted) { } else if (props.isEncrypted) {
content = ( content = (
<Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}>{I18n.t('Encrypted_message')}</Text> <Text
style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}
accessibilityLabel={I18n.t('Encrypted_message')}>
{I18n.t('Encrypted_message')}
</Text>
); );
} else { } else {
const { baseUrl, user, onLinkPress } = useContext(MessageContext); const { baseUrl, user, onLinkPress } = useContext(MessageContext);

View File

@ -0,0 +1,10 @@
export interface IAttachment {
title: string;
type: string;
description: string;
title_link?: string;
image_url?: string;
image_type?: string;
video_url?: string;
video_type?: string;
}

View File

@ -0,0 +1,3 @@
export interface IMessage {
msg: string;
}

View File

@ -0,0 +1,4 @@
export interface IRocketChatRecord {
id: string;
updatedAt: Date;
}

27
app/definitions/IRoom.ts Normal file
View File

@ -0,0 +1,27 @@
import { IRocketChatRecord } from './IRocketChatRecord';
export enum RoomType {
GROUP = 'p',
DIRECT = 'd',
CHANNEL = 'c',
OMNICHANNEL = 'l',
THREAD = 'thread'
}
export interface IRoom extends IRocketChatRecord {
rid: string;
t: RoomType;
name: string;
fname: string;
prid?: string;
tmid?: string;
topic?: string;
teamMain?: boolean;
teamId?: string;
encrypted?: boolean;
visitor?: boolean;
autoTranslateLanguage?: boolean;
autoTranslate?: boolean;
observe?: Function;
usedCannedResponse: string;
}

View File

@ -0,0 +1,16 @@
export interface IServer {
name: string;
iconURL: string;
useRealName: boolean;
FileUpload_MediaTypeWhiteList: string;
FileUpload_MaxFileSize: number;
roomsUpdatedAt: Date;
version: string;
lastLocalAuthenticatedSession: Date;
autoLock: boolean;
autoLockTime: number | null;
biometry: boolean | null;
uniqueID: string;
enterpriseModules: string;
E2E_Enable: boolean;
}

View File

@ -1,5 +1,5 @@
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts // https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
export const TEAM_TYPE = { exports.TEAM_TYPE = {
PUBLIC: 0, PUBLIC: 0,
PRIVATE: 1 PRIVATE: 1
}; };

View File

@ -22,7 +22,7 @@ export interface IDimensionsContextProps {
export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window')); export const DimensionsContext = React.createContext<Partial<IDimensionsContextProps>>(Dimensions.get('window'));
export function withDimensions(Component: any) { export function withDimensions(Component: any): any {
const DimensionsComponent = (props: any) => ( const DimensionsComponent = (props: any) => (
<DimensionsContext.Consumer>{contexts => <Component {...props} {...contexts} />}</DimensionsContext.Consumer> <DimensionsContext.Consumer>{contexts => <Component {...props} {...contexts} />}</DimensionsContext.Consumer>
); );

View File

@ -6,7 +6,6 @@ import { inquiryQueueAdd, inquiryQueueRemove, inquiryQueueUpdate, inquiryRequest
const removeListener = listener => listener.stop(); const removeListener = listener => listener.stop();
let connectedListener; let connectedListener;
let disconnectedListener;
let queueListener; let queueListener;
const streamTopic = 'stream-livechat-inquiry-queue-observer'; const streamTopic = 'stream-livechat-inquiry-queue-observer';
@ -48,10 +47,6 @@ export default function subscribeInquiry() {
connectedListener.then(removeListener); connectedListener.then(removeListener);
connectedListener = false; connectedListener = false;
} }
if (disconnectedListener) {
disconnectedListener.then(removeListener);
disconnectedListener = false;
}
if (queueListener) { if (queueListener) {
queueListener.then(removeListener); queueListener.then(removeListener);
queueListener = false; queueListener = false;
@ -59,7 +54,6 @@ export default function subscribeInquiry() {
}; };
connectedListener = RocketChat.onStreamData('connected', handleConnection); connectedListener = RocketChat.onStreamData('connected', handleConnection);
disconnectedListener = RocketChat.onStreamData('close', handleConnection);
queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived); queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
try { try {

View File

@ -161,4 +161,5 @@ const mapStateToProps = state => ({
showAvatar: state.sortPreferences.showAvatar, showAvatar: state.sortPreferences.showAvatar,
displayMode: state.sortPreferences.displayMode displayMode: state.sortPreferences.displayMode
}); });
export default connect(mapStateToProps)(withDimensions(withTheme(QueueListView))); export default connect(mapStateToProps)(withDimensions(withTheme(QueueListView)));

View File

@ -328,7 +328,6 @@
"N_users": "{{n}} مستخدمين", "N_users": "{{n}} مستخدمين",
"N_channels": "{{n}} القنوات", "N_channels": "{{n}} القنوات",
"Name": "اسم", "Name": "اسم",
"Navigation_history": "تاريخ التصفح",
"Never": "أبداً", "Never": "أبداً",
"New_Message": "رسالة جديدة", "New_Message": "رسالة جديدة",
"New_Password": "كلمة مرور جديدة", "New_Password": "كلمة مرور جديدة",

View File

@ -330,7 +330,6 @@
"N_users": "{{n}} Benutzer", "N_users": "{{n}} Benutzer",
"N_channels": "{{n}} Kanäle", "N_channels": "{{n}} Kanäle",
"Name": "Name", "Name": "Name",
"Navigation_history": "Navigations-Verlauf",
"Never": "Niemals", "Never": "Niemals",
"New_Message": "Neue Nachricht", "New_Message": "Neue Nachricht",
"New_Password": "Neues Kennwort", "New_Password": "Neues Kennwort",

View File

@ -21,6 +21,7 @@
"error-save-video": "Error while saving video", "error-save-video": "Error while saving video",
"error-field-unavailable": "{{field}} is already in use :(", "error-field-unavailable": "{{field}} is already in use :(",
"error-file-too-large": "File is too large", "error-file-too-large": "File is too large",
"error-not-permission-to-upload-file": "You don't have permission to upload files",
"error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.", "error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.",
"error-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}", "error-input-is-not-a-valid-field": "{{input}} is not a valid {{field}}",
"error-invalid-actionlink": "Invalid action link", "error-invalid-actionlink": "Invalid action link",
@ -330,7 +331,6 @@
"N_users": "{{n}} users", "N_users": "{{n}} users",
"N_channels": "{{n}} channels", "N_channels": "{{n}} channels",
"Name": "Name", "Name": "Name",
"Navigation_history": "Navigation history",
"Never": "Never", "Never": "Never",
"New_Message": "New Message", "New_Message": "New Message",
"New_Password": "New Password", "New_Password": "New Password",

View File

@ -330,7 +330,6 @@
"N_users": "{{n}} utilisateurs", "N_users": "{{n}} utilisateurs",
"N_channels": "{{n}} canaux", "N_channels": "{{n}} canaux",
"Name": "Nom", "Name": "Nom",
"Navigation_history": "Historique de navigation",
"Never": "Jamais", "Never": "Jamais",
"New_Message": "Nouveau message", "New_Message": "Nouveau message",
"New_Password": "Nouveau mot de passe", "New_Password": "Nouveau mot de passe",
@ -782,5 +781,8 @@
"No_canned_responses": "Pas de réponses standardisées", "No_canned_responses": "Pas de réponses standardisées",
"Send_email_confirmation": "Envoyer un e-mail de confirmation", "Send_email_confirmation": "Envoyer un e-mail de confirmation",
"sending_email_confirmation": "envoi d'e-mail de confirmation", "sending_email_confirmation": "envoi d'e-mail de confirmation",
"Enable_Message_Parser": "Activer le parseur de messages" "Enable_Message_Parser": "Activer le parseur de messages",
"Unsupported_format": "Format non supporté",
"Downloaded_file": "Fichier téléchargé",
"Error_Download_file": "Erreur lors du téléchargement du fichier"
} }

View File

@ -322,7 +322,6 @@
"N_people_reacted": "{{n}} persone hanno reagito", "N_people_reacted": "{{n}} persone hanno reagito",
"N_users": "{{n}} utenti", "N_users": "{{n}} utenti",
"Name": "Nome", "Name": "Nome",
"Navigation_history": "Cronologia di navigazione",
"Never": "Mai", "Never": "Mai",
"New_Message": "Nuovo messaggio", "New_Message": "Nuovo messaggio",
"New_Password": "Nuova password", "New_Password": "Nuova password",

View File

@ -330,7 +330,6 @@
"N_users": "{{n}} gebruikers", "N_users": "{{n}} gebruikers",
"N_channels": "{{n}} kanalen", "N_channels": "{{n}} kanalen",
"Name": "Naam", "Name": "Naam",
"Navigation_history": "Navigatie geschiedenis",
"Never": "Nooit", "Never": "Nooit",
"New_Message": "Nieuw bericht", "New_Message": "Nieuw bericht",
"New_Password": "Nieuw wachtwoord", "New_Password": "Nieuw wachtwoord",
@ -782,5 +781,8 @@
"No_canned_responses": "Geen standaardantwoorden", "No_canned_responses": "Geen standaardantwoorden",
"Send_email_confirmation": "Stuur e-mailbevestiging", "Send_email_confirmation": "Stuur e-mailbevestiging",
"sending_email_confirmation": "e-mailbevestiging aan het verzenden", "sending_email_confirmation": "e-mailbevestiging aan het verzenden",
"Enable_Message_Parser": "Berichtparser inschakelen" "Enable_Message_Parser": "Berichtparser inschakelen",
"Unsupported_format": "Niet ondersteund formaat",
"Downloaded_file": "Gedownload bestand",
"Error_Download_file": "Fout tijdens het downloaden van bestand"
} }

View File

@ -309,7 +309,6 @@
"N_users": "{{n}} usuários", "N_users": "{{n}} usuários",
"N_channels": "{{n}} canais", "N_channels": "{{n}} canais",
"Name": "Nome", "Name": "Nome",
"Navigation_history": "Histórico de navegação",
"Never": "Nunca", "Never": "Nunca",
"New_Message": "Nova Mensagem", "New_Message": "Nova Mensagem",
"New_Password": "Nova Senha", "New_Password": "Nova Senha",

View File

@ -329,7 +329,6 @@
"N_users": "{{n}} utilizadores", "N_users": "{{n}} utilizadores",
"N_channels": "{{n}} canais", "N_channels": "{{n}} canais",
"Name": "Nome", "Name": "Nome",
"Navigation_history": "Histórico de navegação",
"Never": "Nunca", "Never": "Nunca",
"New_Message": "Nova Mensagem", "New_Message": "Nova Mensagem",
"New_Password": "Nova Palavra-passe", "New_Password": "Nova Palavra-passe",

View File

@ -330,7 +330,6 @@
"N_users": "{{n}} пользователи", "N_users": "{{n}} пользователи",
"N_channels": "{{n}} каналов", "N_channels": "{{n}} каналов",
"Name": "Имя", "Name": "Имя",
"Navigation_history": "История навигации",
"Never": "Никогда", "Never": "Никогда",
"New_Message": "Новое сообщение", "New_Message": "Новое сообщение",
"New_Password": "Новый пароль", "New_Password": "Новый пароль",
@ -782,5 +781,8 @@
"No_canned_responses": "Нет заготовленных ответов", "No_canned_responses": "Нет заготовленных ответов",
"Send_email_confirmation": "Отправить электронное письмо с подтверждением", "Send_email_confirmation": "Отправить электронное письмо с подтверждением",
"sending_email_confirmation": "отправка подтверждения по электронной почте", "sending_email_confirmation": "отправка подтверждения по электронной почте",
"Enable_Message_Parser": "Включить парсер сообщений" "Enable_Message_Parser": "Включить парсер сообщений",
"Unsupported_format": "Неподдерживаемый формат",
"Downloaded_file": "Скачанный файл",
"Error_Download_file": "Ошибка при скачивании файла"
} }

View File

@ -323,7 +323,6 @@
"N_people_reacted": "{{n}} kişi tepki verdi", "N_people_reacted": "{{n}} kişi tepki verdi",
"N_users": "{{n}} kullanıcı", "N_users": "{{n}} kullanıcı",
"Name": "İsim", "Name": "İsim",
"Navigation_history": "Gezinti geçmişi",
"Never": "Asla", "Never": "Asla",
"New_Message": "Yeni İleti", "New_Message": "Yeni İleti",
"New_Password": "Yeni Şifre", "New_Password": "Yeni Şifre",

View File

@ -320,7 +320,6 @@
"N_people_reacted": "{{n}} 人回复", "N_people_reacted": "{{n}} 人回复",
"N_users": "{{n}} 位用户", "N_users": "{{n}} 位用户",
"Name": "名称", "Name": "名称",
"Navigation_history": "浏览历史记录",
"Never": "从不", "Never": "从不",
"New_Message": "新信息", "New_Message": "新信息",
"New_Password": "新密码", "New_Password": "新密码",

View File

@ -321,7 +321,6 @@
"N_people_reacted": "{{n}} 人回复", "N_people_reacted": "{{n}} 人回复",
"N_users": "{{n}} 位使用者", "N_users": "{{n}} 位使用者",
"Name": "名稱", "Name": "名稱",
"Navigation_history": "瀏覽歷史記錄",
"Never": "從不", "Never": "從不",
"New_Message": "新訊息", "New_Message": "新訊息",
"New_Password": "新密碼", "New_Password": "新密碼",

View File

@ -55,7 +55,8 @@ const PERMISSIONS = [
'convert-team', 'convert-team',
'edit-omnichannel-contact', 'edit-omnichannel-contact',
'edit-livechat-room-customfields', 'edit-livechat-room-customfields',
'view-canned-responses' 'view-canned-responses',
'mobile-upload-file'
]; ];
export async function setPermissions() { export async function setPermissions() {

View File

@ -8,7 +8,6 @@ import messagesStatus from '../../../constants/messagesStatus';
import log from '../../../utils/log'; import log from '../../../utils/log';
import random from '../../../utils/random'; import random from '../../../utils/random';
import store from '../../createStore'; import store from '../../createStore';
import { roomsRequest } from '../../../actions/rooms';
import { handlePayloadUserInteraction } from '../actions'; import { handlePayloadUserInteraction } from '../actions';
import buildMessage from '../helpers/buildMessage'; import buildMessage from '../helpers/buildMessage';
import RocketChat from '../../rocketchat'; import RocketChat from '../../rocketchat';
@ -21,8 +20,6 @@ import { E2E_MESSAGE_TYPE } from '../../encryption/constants';
const removeListener = listener => listener.stop(); const removeListener = listener => listener.stop();
let connectedListener;
let disconnectedListener;
let streamListener; let streamListener;
let subServer; let subServer;
let queue = {}; let queue = {};
@ -255,10 +252,6 @@ const debouncedUpdate = subscription => {
}; };
export default function subscribeRooms() { export default function subscribeRooms() {
const handleConnection = () => {
store.dispatch(roomsRequest());
};
const handleStreamMessageReceived = protectedFunction(async ddpMessage => { const handleStreamMessageReceived = protectedFunction(async ddpMessage => {
const db = database.active; const db = database.active;
@ -388,14 +381,6 @@ export default function subscribeRooms() {
}); });
const stop = () => { const stop = () => {
if (connectedListener) {
connectedListener.then(removeListener);
connectedListener = false;
}
if (disconnectedListener) {
disconnectedListener.then(removeListener);
disconnectedListener = false;
}
if (streamListener) { if (streamListener) {
streamListener.then(removeListener); streamListener.then(removeListener);
streamListener = false; streamListener = false;
@ -407,8 +392,6 @@ export default function subscribeRooms() {
} }
}; };
connectedListener = this.sdk.onStreamData('connected', handleConnection);
// disconnectedListener = this.sdk.onStreamData('close', handleConnection);
streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived); streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
try { try {

View File

@ -24,7 +24,7 @@ import { selectServerFailure } from '../actions/server';
import { useSsl } from '../utils/url'; import { useSsl } from '../utils/url';
import EventEmitter from '../utils/events'; import EventEmitter from '../utils/events';
import { updatePermission } from '../actions/permissions'; import { updatePermission } from '../actions/permissions';
import { TEAM_TYPE } from '../definition/ITeam'; import { TEAM_TYPE } from '../definitions/ITeam';
import { updateSettings } from '../actions/settings'; import { updateSettings } from '../actions/settings';
import { compareServerVersion, methods } from './utils'; import { compareServerVersion, methods } from './utils';
import reduxStore from './createStore'; import reduxStore from './createStore';
@ -239,37 +239,34 @@ const RocketChat = {
this.code = null; this.code = null;
} }
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server) }); // The app can't reconnect if reopen interval is 5s while in development
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: useSsl(server), reopen: __DEV__ ? 20000 : 5000 });
this.getSettings(); this.getSettings();
const sdkConnect = () => this.sdk
this.sdk .connect()
.connect() .then(() => {
.then(() => { console.log('connected');
const { server: currentServer } = reduxStore.getState().server; })
if (user && user.token && server === currentServer) { .catch(err => {
reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError)); console.log('connect error', err);
} });
})
.catch(err => {
console.log('connect error', err);
// when `connect` raises an error, we try again in 10 seconds
this.connectTimeout = setTimeout(() => {
if (this.sdk?.client?.host === server) {
sdkConnect();
}
}, 10000);
});
sdkConnect();
this.connectingListener = this.sdk.onStreamData('connecting', () => { this.connectingListener = this.sdk.onStreamData('connecting', () => {
reduxStore.dispatch(connectRequest()); reduxStore.dispatch(connectRequest());
}); });
this.connectedListener = this.sdk.onStreamData('connected', () => { this.connectedListener = this.sdk.onStreamData('connected', () => {
const { connected } = reduxStore.getState().meteor;
if (connected) {
return;
}
reduxStore.dispatch(connectSuccess()); reduxStore.dispatch(connectSuccess());
const { server: currentServer } = reduxStore.getState().server;
const { user } = reduxStore.getState().login;
if (user?.token && server === currentServer) {
reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError));
}
}); });
this.closeListener = this.sdk.onStreamData('close', () => { this.closeListener = this.sdk.onStreamData('close', () => {
@ -1141,10 +1138,6 @@ const RocketChat = {
// RC 0.36.0 // RC 0.36.0
return this.methodCallWrapper('livechat:transfer', transferData); return this.methodCallWrapper('livechat:transfer', transferData);
}, },
getPagesLivechat(rid, offset) {
// RC 2.3.0
return this.sdk.get(`livechat/visitors.pagesVisited/${rid}?count=50&offset=${offset}`);
},
getDepartmentInfo(departmentId) { getDepartmentInfo(departmentId) {
// RC 2.2.0 // RC 2.2.0
return this.sdk.get(`livechat/department/${departmentId}?includeAgents=false`); return this.sdk.get(`livechat/department/${departmentId}?includeAgents=false`);
@ -1523,16 +1516,7 @@ const RocketChat = {
return this.sdk.get(`${this.roomTypeToApiType(type)}.files`, { return this.sdk.get(`${this.roomTypeToApiType(type)}.files`, {
roomId, roomId,
offset, offset,
sort: { uploadedAt: -1 }, sort: { uploadedAt: -1 }
fields: {
name: 1,
description: 1,
size: 1,
type: 1,
uploadedAt: 1,
url: 1,
userId: 1
}
}); });
}, },
getMessages(roomId, type, query, offset) { getMessages(roomId, type, query, offset) {

45
app/navigationTypes.ts Normal file
View File

@ -0,0 +1,45 @@
import { NavigatorScreenParams } from '@react-navigation/core';
import { IRoom } from './definitions/IRoom';
import { IServer } from './definitions/IServer';
import { IAttachment } from './definitions/IAttachment';
import { MasterDetailInsideStackParamList } from './stacks/MasterDetailStack/types';
import { OutsideParamList, InsideStackParamList } from './stacks/types';
export type SetUsernameStackParamList = {
SetUsernameView: {
title: string;
};
};
export type StackParamList = {
AuthLoading: undefined;
OutsideStack: NavigatorScreenParams<OutsideParamList>;
InsideStack: NavigatorScreenParams<InsideStackParamList>;
MasterDetailStack: NavigatorScreenParams<MasterDetailInsideStackParamList>;
SetUsernameStack: NavigatorScreenParams<SetUsernameStackParamList>;
};
export type ShareInsideStackParamList = {
ShareListView: undefined;
ShareView: {
attachments: IAttachment[];
isShareView?: boolean;
isShareExtension: boolean;
serverInfo: IServer;
text: string;
room: IRoom;
thread: any; // TODO: Change
};
SelectServerView: undefined;
};
export type ShareOutsideStackParamList = {
WithoutServersView: undefined;
};
export type ShareAppStackParamList = {
AuthLoading?: undefined;
OutsideStack?: NavigatorScreenParams<ShareOutsideStackParamList>;
InsideStack?: NavigatorScreenParams<ShareInsideStackParamList>;
};

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Text, View } from 'react-native'; import { Text, View, ViewStyle } from 'react-native';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
@ -10,7 +10,7 @@ import { themes } from '../../constants/colors';
export { ROW_HEIGHT }; export { ROW_HEIGHT };
interface IDirectoryItemLabel { interface IDirectoryItemLabel {
text: string; text?: string;
theme: string; theme: string;
} }
@ -21,9 +21,9 @@ interface IDirectoryItem {
type: string; type: string;
onPress(): void; onPress(): void;
testID: string; testID: string;
style: any; style?: ViewStyle;
rightLabel: string; rightLabel?: string;
rid: string; rid?: string;
theme: string; theme: string;
teamMain?: boolean; teamMain?: boolean;
} }
@ -32,7 +32,7 @@ const DirectoryItemLabel = React.memo(({ text, theme }: IDirectoryItemLabel) =>
if (!text) { if (!text) {
return null; return null;
} }
return <Text style={[styles.directoryItemLabel, { color: themes[theme!].auxiliaryText }]}>{text}</Text>; return <Text style={[styles.directoryItemLabel, { color: themes[theme].auxiliaryText }]}>{text}</Text>;
}); });
const DirectoryItem = ({ const DirectoryItem = ({

View File

@ -4,7 +4,7 @@ import { KeyboardAwareScrollView, KeyboardAwareScrollViewProps } from '@codler/r
import scrollPersistTaps from '../utils/scrollPersistTaps'; import scrollPersistTaps from '../utils/scrollPersistTaps';
interface IKeyboardViewProps extends KeyboardAwareScrollViewProps { interface IKeyboardViewProps extends KeyboardAwareScrollViewProps {
keyboardVerticalOffset: number; keyboardVerticalOffset?: number;
scrollEnabled?: boolean; scrollEnabled?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }

View File

@ -46,7 +46,7 @@ interface IUserItem {
testID: string; testID: string;
onLongPress?: () => void; onLongPress?: () => void;
style?: StyleProp<ViewStyle>; style?: StyleProp<ViewStyle>;
icon: string; icon?: string | null;
theme: string; theme: string;
} }

View File

@ -25,6 +25,7 @@ import { setCurrentScreen } from './utils/log';
import AuthLoadingView from './views/AuthLoadingView'; import AuthLoadingView from './views/AuthLoadingView';
import { DimensionsContext } from './dimensions'; import { DimensionsContext } from './dimensions';
import debounce from './utils/debounce'; import debounce from './utils/debounce';
import { ShareInsideStackParamList, ShareOutsideStackParamList, ShareAppStackParamList } from './navigationTypes';
interface IDimensions { interface IDimensions {
width: number; width: number;
@ -46,7 +47,7 @@ interface IState {
fontScale: number; fontScale: number;
} }
const Inside = createStackNavigator(); const Inside = createStackNavigator<ShareInsideStackParamList>();
const InsideStack = () => { const InsideStack = () => {
const { theme } = useContext(ThemeContext); const { theme } = useContext(ThemeContext);
@ -65,24 +66,19 @@ const InsideStack = () => {
); );
}; };
const Outside = createStackNavigator(); const Outside = createStackNavigator<ShareOutsideStackParamList>();
const OutsideStack = () => { const OutsideStack = () => {
const { theme } = useContext(ThemeContext); const { theme } = useContext(ThemeContext);
return ( return (
<Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme) }}> <Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme) }}>
<Outside.Screen <Outside.Screen name='WithoutServersView' component={WithoutServersView} options={WithoutServersView.navigationOptions} />
name='WithoutServersView'
component={WithoutServersView}
/* @ts-ignore*/
options={WithoutServersView.navigationOptions}
/>
</Outside.Navigator> </Outside.Navigator>
); );
}; };
// App // App
const Stack = createStackNavigator(); const Stack = createStackNavigator<ShareAppStackParamList>();
export const App = ({ root }: any) => ( export const App = ({ root }: any) => (
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}> <Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
<> <>
@ -112,7 +108,7 @@ class Root extends React.Component<{}, IState> {
this.init(); this.init();
} }
componentWillUnmount() { componentWillUnmount(): void {
RocketChat.closeShareExtension(); RocketChat.closeShareExtension();
unsubscribeTheme(); unsubscribeTheme();
} }

View File

@ -1,12 +1,11 @@
import React from 'react'; import React from 'react';
import { I18nManager } from 'react-native'; import { I18nManager } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator, StackNavigationOptions } from '@react-navigation/stack';
import { createDrawerNavigator } from '@react-navigation/drawer'; import { createDrawerNavigator } from '@react-navigation/drawer';
import { ThemeContext } from '../theme'; import { ThemeContext } from '../theme';
import { ModalAnimation, StackAnimation, defaultHeader, themedHeader } from '../utils/navigation'; import { ModalAnimation, StackAnimation, defaultHeader, themedHeader } from '../utils/navigation';
import Sidebar from '../views/SidebarView'; import Sidebar from '../views/SidebarView';
// Chats Stack // Chats Stack
import RoomView from '../views/RoomView'; import RoomView from '../views/RoomView';
import RoomsListView from '../views/RoomsListView'; import RoomsListView from '../views/RoomsListView';
@ -22,7 +21,6 @@ import MessagesView from '../views/MessagesView';
import AutoTranslateView from '../views/AutoTranslateView'; import AutoTranslateView from '../views/AutoTranslateView';
import DirectoryView from '../views/DirectoryView'; import DirectoryView from '../views/DirectoryView';
import NotificationPrefView from '../views/NotificationPreferencesView'; import NotificationPrefView from '../views/NotificationPreferencesView';
import VisitorNavigationView from '../views/VisitorNavigationView';
import ForwardLivechatView from '../views/ForwardLivechatView'; import ForwardLivechatView from '../views/ForwardLivechatView';
import LivechatEditView from '../views/LivechatEditView'; import LivechatEditView from '../views/LivechatEditView';
import PickerView from '../views/PickerView'; import PickerView from '../views/PickerView';
@ -37,10 +35,8 @@ import { themes } from '../constants/colors';
import ProfileView from '../views/ProfileView'; import ProfileView from '../views/ProfileView';
import UserPreferencesView from '../views/UserPreferencesView'; import UserPreferencesView from '../views/UserPreferencesView';
import UserNotificationPrefView from '../views/UserNotificationPreferencesView'; import UserNotificationPrefView from '../views/UserNotificationPreferencesView';
// Display Preferences View // Display Preferences View
import DisplayPrefsView from '../views/DisplayPrefsView'; import DisplayPrefsView from '../views/DisplayPrefsView';
// Settings Stack // Settings Stack
import SettingsView from '../views/SettingsView'; import SettingsView from '../views/SettingsView';
import SecurityPrivacyView from '../views/SecurityPrivacyView'; import SecurityPrivacyView from '../views/SecurityPrivacyView';
@ -49,21 +45,16 @@ import LanguageView from '../views/LanguageView';
import ThemeView from '../views/ThemeView'; import ThemeView from '../views/ThemeView';
import DefaultBrowserView from '../views/DefaultBrowserView'; import DefaultBrowserView from '../views/DefaultBrowserView';
import ScreenLockConfigView from '../views/ScreenLockConfigView'; import ScreenLockConfigView from '../views/ScreenLockConfigView';
// Admin Stack // Admin Stack
import AdminPanelView from '../views/AdminPanelView'; import AdminPanelView from '../views/AdminPanelView';
// NewMessage Stack // NewMessage Stack
import NewMessageView from '../views/NewMessageView'; import NewMessageView from '../views/NewMessageView';
import CreateChannelView from '../views/CreateChannelView'; import CreateChannelView from '../views/CreateChannelView';
// E2ESaveYourPassword Stack // E2ESaveYourPassword Stack
import E2ESaveYourPasswordView from '../views/E2ESaveYourPasswordView'; import E2ESaveYourPasswordView from '../views/E2ESaveYourPasswordView';
import E2EHowItWorksView from '../views/E2EHowItWorksView'; import E2EHowItWorksView from '../views/E2EHowItWorksView';
// E2EEnterYourPassword Stack // E2EEnterYourPassword Stack
import E2EEnterYourPasswordView from '../views/E2EEnterYourPasswordView'; import E2EEnterYourPasswordView from '../views/E2EEnterYourPasswordView';
// InsideStackNavigator // InsideStackNavigator
import AttachmentView from '../views/AttachmentView'; import AttachmentView from '../views/AttachmentView';
import ModalBlockView from '../views/ModalBlockView'; import ModalBlockView from '../views/ModalBlockView';
@ -75,20 +66,33 @@ import QueueListView from '../ee/omnichannel/views/QueueListView';
import AddChannelTeamView from '../views/AddChannelTeamView'; import AddChannelTeamView from '../views/AddChannelTeamView';
import AddExistingChannelView from '../views/AddExistingChannelView'; import AddExistingChannelView from '../views/AddExistingChannelView';
import SelectListView from '../views/SelectListView'; import SelectListView from '../views/SelectListView';
import {
AdminPanelStackParamList,
ChatsStackParamList,
DisplayPrefStackParamList,
DrawerParamList,
E2EEnterYourPasswordStackParamList,
E2ESaveYourPasswordStackParamList,
InsideStackParamList,
NewMessageStackParamList,
ProfileStackParamList,
SettingsStackParamList
} from './types';
// ChatsStackNavigator // ChatsStackNavigator
const ChatsStack = createStackNavigator(); const ChatsStack = createStackNavigator<ChatsStackParamList>();
const ChatsStackNavigator = () => { const ChatsStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<ChatsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <ChatsStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<ChatsStack.Screen name='RoomsListView' component={RoomsListView} /> <ChatsStack.Screen name='RoomsListView' component={RoomsListView} />
<ChatsStack.Screen name='RoomView' component={RoomView} /> <ChatsStack.Screen name='RoomView' component={RoomView} />
<ChatsStack.Screen name='RoomActionsView' component={RoomActionsView} options={RoomActionsView.navigationOptions} /> <ChatsStack.Screen name='RoomActionsView' component={RoomActionsView} options={RoomActionsView.navigationOptions} />
<ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} /> <ChatsStack.Screen name='SelectListView' component={SelectListView} options={SelectListView.navigationOptions} />
<ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} /> <ChatsStack.Screen name='RoomInfoView' component={RoomInfoView} options={RoomInfoView.navigationOptions} />
<ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} /> <ChatsStack.Screen name='RoomInfoEditView' component={RoomInfoEditView} options={RoomInfoEditView.navigationOptions} />
<ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} options={RoomMembersView.navigationOptions} /> <ChatsStack.Screen name='RoomMembersView' component={RoomMembersView} />
<ChatsStack.Screen <ChatsStack.Screen
name='SearchMessagesView' name='SearchMessagesView'
component={SearchMessagesView} component={SearchMessagesView}
@ -109,11 +113,6 @@ const ChatsStackNavigator = () => {
component={NotificationPrefView} component={NotificationPrefView}
options={NotificationPrefView.navigationOptions} options={NotificationPrefView.navigationOptions}
/> />
<ChatsStack.Screen
name='VisitorNavigationView'
component={VisitorNavigationView}
options={VisitorNavigationView.navigationOptions}
/>
<ChatsStack.Screen <ChatsStack.Screen
name='ForwardLivechatView' name='ForwardLivechatView'
component={ForwardLivechatView} component={ForwardLivechatView}
@ -126,13 +125,9 @@ const ChatsStackNavigator = () => {
component={ThreadMessagesView} component={ThreadMessagesView}
options={ThreadMessagesView.navigationOptions} options={ThreadMessagesView.navigationOptions}
/> />
<ChatsStack.Screen name='TeamChannelsView' component={TeamChannelsView} options={TeamChannelsView.navigationOptions} /> <ChatsStack.Screen name='TeamChannelsView' component={TeamChannelsView} />
<ChatsStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} /> <ChatsStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} />
<ChatsStack.Screen <ChatsStack.Screen name='AddChannelTeamView' component={AddChannelTeamView} />
name='AddChannelTeamView'
component={AddChannelTeamView}
options={AddChannelTeamView.navigationOptions}
/>
<ChatsStack.Screen <ChatsStack.Screen
name='AddExistingChannelView' name='AddExistingChannelView'
component={AddExistingChannelView} component={AddExistingChannelView}
@ -141,32 +136,21 @@ const ChatsStackNavigator = () => {
<ChatsStack.Screen name='MarkdownTableView' component={MarkdownTableView} options={MarkdownTableView.navigationOptions} /> <ChatsStack.Screen name='MarkdownTableView' component={MarkdownTableView} options={MarkdownTableView.navigationOptions} />
<ChatsStack.Screen name='ReadReceiptsView' component={ReadReceiptsView} options={ReadReceiptsView.navigationOptions} /> <ChatsStack.Screen name='ReadReceiptsView' component={ReadReceiptsView} options={ReadReceiptsView.navigationOptions} />
<ChatsStack.Screen name='QueueListView' component={QueueListView} options={QueueListView.navigationOptions} /> <ChatsStack.Screen name='QueueListView' component={QueueListView} options={QueueListView.navigationOptions} />
<ChatsStack.Screen <ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
name='CannedResponsesListView' <ChatsStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
component={CannedResponsesListView}
options={CannedResponsesListView.navigationOptions}
/>
<ChatsStack.Screen
name='CannedResponseDetail'
component={CannedResponseDetail}
options={CannedResponseDetail.navigationOptions}
/>
</ChatsStack.Navigator> </ChatsStack.Navigator>
); );
}; };
// ProfileStackNavigator // ProfileStackNavigator
const ProfileStack = createStackNavigator(); const ProfileStack = createStackNavigator<ProfileStackParamList>();
const ProfileStackNavigator = () => { const ProfileStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<ProfileStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <ProfileStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<ProfileStack.Screen name='ProfileView' component={ProfileView} options={ProfileView.navigationOptions} /> <ProfileStack.Screen name='ProfileView' component={ProfileView} options={ProfileView.navigationOptions} />
<ProfileStack.Screen <ProfileStack.Screen name='UserPreferencesView' component={UserPreferencesView} />
name='UserPreferencesView'
component={UserPreferencesView}
options={UserPreferencesView.navigationOptions}
/>
<ProfileStack.Screen <ProfileStack.Screen
name='UserNotificationPrefView' name='UserNotificationPrefView'
component={UserNotificationPrefView} component={UserNotificationPrefView}
@ -178,18 +162,15 @@ const ProfileStackNavigator = () => {
}; };
// SettingsStackNavigator // SettingsStackNavigator
const SettingsStack = createStackNavigator(); const SettingsStack = createStackNavigator<SettingsStackParamList>();
const SettingsStackNavigator = () => { const SettingsStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<SettingsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <SettingsStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<SettingsStack.Screen name='SettingsView' component={SettingsView} options={SettingsView.navigationOptions} /> <SettingsStack.Screen name='SettingsView' component={SettingsView} options={SettingsView.navigationOptions} />
<SettingsStack.Screen <SettingsStack.Screen name='SecurityPrivacyView' component={SecurityPrivacyView} />
name='SecurityPrivacyView'
component={SecurityPrivacyView}
options={SecurityPrivacyView.navigationOptions}
/>
<SettingsStack.Screen <SettingsStack.Screen
name='E2EEncryptionSecurityView' name='E2EEncryptionSecurityView'
component={E2EEncryptionSecurityView} component={E2EEncryptionSecurityView}
@ -212,31 +193,33 @@ const SettingsStackNavigator = () => {
}; };
// AdminPanelStackNavigator // AdminPanelStackNavigator
const AdminPanelStack = createStackNavigator(); const AdminPanelStack = createStackNavigator<AdminPanelStackParamList>();
const AdminPanelStackNavigator = () => { const AdminPanelStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<AdminPanelStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <AdminPanelStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<AdminPanelStack.Screen name='AdminPanelView' component={AdminPanelView} options={AdminPanelView.navigationOptions} /> <AdminPanelStack.Screen name='AdminPanelView' component={AdminPanelView} options={AdminPanelView.navigationOptions} />
</AdminPanelStack.Navigator> </AdminPanelStack.Navigator>
); );
}; };
// DisplayPreferenceNavigator // DisplayPreferenceNavigator
const DisplayPrefStack = createStackNavigator(); const DisplayPrefStack = createStackNavigator<DisplayPrefStackParamList>();
const DisplayPrefStackNavigator = () => { const DisplayPrefStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<DisplayPrefStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <DisplayPrefStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<DisplayPrefStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} /> <DisplayPrefStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} />
</DisplayPrefStack.Navigator> </DisplayPrefStack.Navigator>
); );
}; };
// DrawerNavigator // DrawerNavigator
const Drawer = createDrawerNavigator(); const Drawer = createDrawerNavigator<DrawerParamList>();
const DrawerNavigator = () => { const DrawerNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
@ -257,12 +240,13 @@ const DrawerNavigator = () => {
}; };
// NewMessageStackNavigator // NewMessageStackNavigator
const NewMessageStack = createStackNavigator(); const NewMessageStack = createStackNavigator<NewMessageStackParamList>();
const NewMessageStackNavigator = () => { const NewMessageStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<NewMessageStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <NewMessageStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<NewMessageStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} /> <NewMessageStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} />
<NewMessageStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} /> <NewMessageStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} />
<NewMessageStack.Screen <NewMessageStack.Screen
@ -276,12 +260,13 @@ const NewMessageStackNavigator = () => {
}; };
// E2ESaveYourPasswordStackNavigator // E2ESaveYourPasswordStackNavigator
const E2ESaveYourPasswordStack = createStackNavigator(); const E2ESaveYourPasswordStack = createStackNavigator<E2ESaveYourPasswordStackParamList>();
const E2ESaveYourPasswordStackNavigator = () => { const E2ESaveYourPasswordStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<E2ESaveYourPasswordStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <E2ESaveYourPasswordStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<E2ESaveYourPasswordStack.Screen <E2ESaveYourPasswordStack.Screen
name='E2ESaveYourPasswordView' name='E2ESaveYourPasswordView'
component={E2ESaveYourPasswordView} component={E2ESaveYourPasswordView}
@ -297,12 +282,13 @@ const E2ESaveYourPasswordStackNavigator = () => {
}; };
// E2EEnterYourPasswordStackNavigator // E2EEnterYourPasswordStackNavigator
const E2EEnterYourPasswordStack = createStackNavigator(); const E2EEnterYourPasswordStack = createStackNavigator<E2EEnterYourPasswordStackParamList>();
const E2EEnterYourPasswordStackNavigator = () => { const E2EEnterYourPasswordStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<E2EEnterYourPasswordStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <E2EEnterYourPasswordStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<E2EEnterYourPasswordStack.Screen <E2EEnterYourPasswordStack.Screen
name='E2EEnterYourPasswordView' name='E2EEnterYourPasswordView'
component={E2EEnterYourPasswordView} component={E2EEnterYourPasswordView}
@ -313,7 +299,7 @@ const E2EEnterYourPasswordStackNavigator = () => {
}; };
// InsideStackNavigator // InsideStackNavigator
const InsideStack = createStackNavigator(); const InsideStack = createStackNavigator<InsideStackParamList>();
const InsideStackNavigator = () => { const InsideStackNavigator = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);

View File

@ -1,10 +1,17 @@
import React from 'react'; import React from 'react';
import { StyleSheet, TouchableWithoutFeedback, View } from 'react-native'; import { StyleSheet, TouchableWithoutFeedback, View } from 'react-native';
import PropTypes from 'prop-types'; import { StackNavigationProp } from '@react-navigation/stack';
import { NavigationContainerProps } from '@react-navigation/core';
import sharedStyles from '../../views/Styles'; import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
interface IModalContainer extends NavigationContainerProps {
navigation: StackNavigationProp<any>;
children: React.ReactNode;
theme: string;
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
root: { root: {
flex: 1, flex: 1,
@ -12,11 +19,11 @@ const styles = StyleSheet.create({
justifyContent: 'center' justifyContent: 'center'
}, },
backdrop: { backdrop: {
...StyleSheet.absoluteFill ...StyleSheet.absoluteFillObject
} }
}); });
export const ModalContainer = ({ navigation, children, theme }) => ( export const ModalContainer = ({ navigation, children, theme }: IModalContainer): JSX.Element => (
<View style={[styles.root, { backgroundColor: `${themes[theme].backdropColor}70` }]}> <View style={[styles.root, { backgroundColor: `${themes[theme].backdropColor}70` }]}>
<TouchableWithoutFeedback onPress={() => navigation.pop()}> <TouchableWithoutFeedback onPress={() => navigation.pop()}>
<View style={styles.backdrop} /> <View style={styles.backdrop} />
@ -24,9 +31,3 @@ export const ModalContainer = ({ navigation, children, theme }) => (
<View style={sharedStyles.modalFormSheet}>{children}</View> <View style={sharedStyles.modalFormSheet}>{children}</View>
</View> </View>
); );
ModalContainer.propTypes = {
navigation: PropTypes.object,
children: PropTypes.element,
theme: PropTypes.string
};

View File

@ -1,12 +1,10 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useIsFocused } from '@react-navigation/native'; import { useIsFocused } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { createDrawerNavigator } from '@react-navigation/drawer'; import { createDrawerNavigator } from '@react-navigation/drawer';
import { ThemeContext } from '../../theme'; import { ThemeContext } from '../../theme';
import { FadeFromCenterModal, StackAnimation, defaultHeader, themedHeader } from '../../utils/navigation'; import { FadeFromCenterModal, StackAnimation, defaultHeader, themedHeader } from '../../utils/navigation';
// Chats Stack // Chats Stack
import RoomView from '../../views/RoomView'; import RoomView from '../../views/RoomView';
import RoomsListView from '../../views/RoomsListView'; import RoomsListView from '../../views/RoomsListView';
@ -22,7 +20,6 @@ import MessagesView from '../../views/MessagesView';
import AutoTranslateView from '../../views/AutoTranslateView'; import AutoTranslateView from '../../views/AutoTranslateView';
import DirectoryView from '../../views/DirectoryView'; import DirectoryView from '../../views/DirectoryView';
import NotificationPrefView from '../../views/NotificationPreferencesView'; import NotificationPrefView from '../../views/NotificationPreferencesView';
import VisitorNavigationView from '../../views/VisitorNavigationView';
import ForwardLivechatView from '../../views/ForwardLivechatView'; import ForwardLivechatView from '../../views/ForwardLivechatView';
import CannedResponsesListView from '../../views/CannedResponsesListView'; import CannedResponsesListView from '../../views/CannedResponsesListView';
import CannedResponseDetail from '../../views/CannedResponseDetail'; import CannedResponseDetail from '../../views/CannedResponseDetail';
@ -46,7 +43,6 @@ import UserPreferencesView from '../../views/UserPreferencesView';
import UserNotificationPrefView from '../../views/UserNotificationPreferencesView'; import UserNotificationPrefView from '../../views/UserNotificationPreferencesView';
import SecurityPrivacyView from '../../views/SecurityPrivacyView'; import SecurityPrivacyView from '../../views/SecurityPrivacyView';
import E2EEncryptionSecurityView from '../../views/E2EEncryptionSecurityView'; import E2EEncryptionSecurityView from '../../views/E2EEncryptionSecurityView';
// InsideStackNavigator // InsideStackNavigator
import AttachmentView from '../../views/AttachmentView'; import AttachmentView from '../../views/AttachmentView';
import ModalBlockView from '../../views/ModalBlockView'; import ModalBlockView from '../../views/ModalBlockView';
@ -63,9 +59,15 @@ import AddChannelTeamView from '../../views/AddChannelTeamView';
import AddExistingChannelView from '../../views/AddExistingChannelView'; import AddExistingChannelView from '../../views/AddExistingChannelView';
import SelectListView from '../../views/SelectListView'; import SelectListView from '../../views/SelectListView';
import { ModalContainer } from './ModalContainer'; import { ModalContainer } from './ModalContainer';
import {
MasterDetailChatsStackParamList,
MasterDetailDrawerParamList,
MasterDetailInsideStackParamList,
ModalStackParamList
} from './types';
// ChatsStackNavigator // ChatsStackNavigator
const ChatsStack = createStackNavigator(); const ChatsStack = createStackNavigator<MasterDetailChatsStackParamList>();
const ChatsStackNavigator = React.memo(() => { const ChatsStackNavigator = React.memo(() => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
@ -82,28 +84,35 @@ const ChatsStackNavigator = React.memo(() => {
}, [isFocused]); }, [isFocused]);
return ( return (
<ChatsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <ChatsStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<ChatsStack.Screen name='RoomView' component={RoomView} options={{ headerShown: false }} /> <ChatsStack.Screen name='RoomView' component={RoomView} options={{ headerShown: false }} />
</ChatsStack.Navigator> </ChatsStack.Navigator>
); );
}); });
// DrawerNavigator // DrawerNavigator
const Drawer = createDrawerNavigator(); const Drawer = createDrawerNavigator<MasterDetailDrawerParamList>();
const DrawerNavigator = React.memo(() => ( const DrawerNavigator = React.memo(() => (
<Drawer.Navigator <Drawer.Navigator
// @ts-ignore
drawerContent={({ navigation, state }) => <RoomsListView navigation={navigation} state={state} />} drawerContent={({ navigation, state }) => <RoomsListView navigation={navigation} state={state} />}
drawerType='permanent'> drawerType='permanent'>
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} /> <Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />
</Drawer.Navigator> </Drawer.Navigator>
)); ));
const ModalStack = createStackNavigator(); export interface INavigation {
const ModalStackNavigator = React.memo(({ navigation }) => { navigation: StackNavigationProp<ModalStackParamList>;
}
const ModalStack = createStackNavigator<ModalStackParamList>();
const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<ModalContainer navigation={navigation} theme={theme}> <ModalContainer navigation={navigation} theme={theme}>
<ModalStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <ModalStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<ModalStack.Screen <ModalStack.Screen
name='RoomActionsView' name='RoomActionsView'
component={RoomActionsView} component={RoomActionsView}
@ -120,11 +129,7 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
/> />
<ModalStack.Screen name='SelectedUsersView' component={SelectedUsersView} /> <ModalStack.Screen name='SelectedUsersView' component={SelectedUsersView} />
<ModalStack.Screen name='InviteUsersView' component={InviteUsersView} options={InviteUsersView.navigationOptions} /> <ModalStack.Screen name='InviteUsersView' component={InviteUsersView} options={InviteUsersView.navigationOptions} />
<ModalStack.Screen <ModalStack.Screen name='AddChannelTeamView' component={AddChannelTeamView} />
name='AddChannelTeamView'
component={AddChannelTeamView}
options={AddChannelTeamView.navigationOptions}
/>
<ModalStack.Screen <ModalStack.Screen
name='AddExistingChannelView' name='AddExistingChannelView'
component={AddExistingChannelView} component={AddExistingChannelView}
@ -152,26 +157,13 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
component={NotificationPrefView} component={NotificationPrefView}
options={NotificationPrefView.navigationOptions} options={NotificationPrefView.navigationOptions}
/> />
<ModalStack.Screen
name='VisitorNavigationView'
component={VisitorNavigationView}
options={VisitorNavigationView.navigationOptions}
/>
<ModalStack.Screen <ModalStack.Screen
name='ForwardLivechatView' name='ForwardLivechatView'
component={ForwardLivechatView} component={ForwardLivechatView}
options={ForwardLivechatView.navigationOptions} options={ForwardLivechatView.navigationOptions}
/> />
<ModalStack.Screen <ModalStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
name='CannedResponsesListView' <ModalStack.Screen name='CannedResponseDetail' component={CannedResponseDetail} />
component={CannedResponsesListView}
options={CannedResponsesListView.navigationOptions}
/>
<ModalStack.Screen
name='CannedResponseDetail'
component={CannedResponseDetail}
options={CannedResponseDetail.navigationOptions}
/>
<ModalStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} /> <ModalStack.Screen name='LivechatEditView' component={LivechatEditView} options={LivechatEditView.navigationOptions} />
<ModalStack.Screen name='PickerView' component={PickerView} options={PickerView.navigationOptions} /> <ModalStack.Screen name='PickerView' component={PickerView} options={PickerView.navigationOptions} />
<ModalStack.Screen name='ThreadMessagesView' component={ThreadMessagesView} /> <ModalStack.Screen name='ThreadMessagesView' component={ThreadMessagesView} />
@ -226,21 +218,13 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
component={E2EEnterYourPasswordView} component={E2EEnterYourPasswordView}
options={E2EEnterYourPasswordView.navigationOptions} options={E2EEnterYourPasswordView.navigationOptions}
/> />
<ModalStack.Screen <ModalStack.Screen name='UserPreferencesView' component={UserPreferencesView} />
name='UserPreferencesView'
component={UserPreferencesView}
options={UserPreferencesView.navigationOptions}
/>
<ModalStack.Screen <ModalStack.Screen
name='UserNotificationPrefView' name='UserNotificationPrefView'
component={UserNotificationPrefView} component={UserNotificationPrefView}
options={UserNotificationPrefView.navigationOptions} options={UserNotificationPrefView.navigationOptions}
/> />
<ModalStack.Screen <ModalStack.Screen name='SecurityPrivacyView' component={SecurityPrivacyView} />
name='SecurityPrivacyView'
component={SecurityPrivacyView}
options={SecurityPrivacyView.navigationOptions}
/>
<ModalStack.Screen <ModalStack.Screen
name='E2EEncryptionSecurityView' name='E2EEncryptionSecurityView'
component={E2EEncryptionSecurityView} component={E2EEncryptionSecurityView}
@ -251,16 +235,14 @@ const ModalStackNavigator = React.memo(({ navigation }) => {
); );
}); });
ModalStackNavigator.propTypes = {
navigation: PropTypes.object
};
// InsideStackNavigator // InsideStackNavigator
const InsideStack = createStackNavigator(); const InsideStack = createStackNavigator<MasterDetailInsideStackParamList>();
const InsideStackNavigator = React.memo(() => { const InsideStackNavigator = React.memo(() => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<InsideStack.Navigator mode='modal' screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...FadeFromCenterModal }}> <InsideStack.Navigator
mode='modal'
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...FadeFromCenterModal } as StackNavigationOptions}>
<InsideStack.Screen name='DrawerNavigator' component={DrawerNavigator} options={{ headerShown: false }} /> <InsideStack.Screen name='DrawerNavigator' component={DrawerNavigator} options={{ headerShown: false }} />
<InsideStack.Screen name='ModalStackNavigator' component={ModalStackNavigator} options={{ headerShown: false }} /> <InsideStack.Screen name='ModalStackNavigator' component={ModalStackNavigator} options={{ headerShown: false }} />
<InsideStack.Screen name='AttachmentView' component={AttachmentView} /> <InsideStack.Screen name='AttachmentView' component={AttachmentView} />

View File

@ -0,0 +1,200 @@
import { TextInputProps } from 'react-native';
import { NavigatorScreenParams } from '@react-navigation/core';
import { IAttachment } from '../../definitions/IAttachment';
import { IMessage } from '../../definitions/IMessage';
import { IRoom, RoomType } from '../../definitions/IRoom';
export type MasterDetailChatsStackParamList = {
RoomView: {
rid: string;
t: RoomType;
tmid?: string;
message?: string;
name?: string;
fname?: string;
prid?: string;
room: IRoom;
jumpToMessageId?: string;
jumpToThreadId?: string;
roomUserId?: string;
};
};
export type MasterDetailDrawerParamList = {
ChatsStackNavigator: NavigatorScreenParams<MasterDetailChatsStackParamList>;
};
export type ModalStackParamList = {
RoomActionsView: {
room: IRoom;
member: any;
rid: string;
t: RoomType;
joined: boolean;
};
RoomInfoView: {
room: IRoom;
member: any;
rid: string;
t: RoomType;
};
SelectListView: {
data: any;
title: string;
infoText: string;
nextAction: Function;
showAlert: boolean;
isSearch: boolean;
onSearch: Function;
isRadio?: boolean;
};
RoomInfoEditView: {
rid: string;
};
RoomMembersView: {
rid: string;
room: IRoom;
};
SearchMessagesView: {
rid: string;
t: RoomType;
encrypted?: boolean;
showCloseModal?: boolean;
};
SelectedUsersView: {
maxUsers: number;
showButton: boolean;
title: string;
buttonText: string;
nextAction: Function;
};
InviteUsersView: {
rid: string;
};
AddChannelTeamView: {
teamId?: string;
teamChannels: []; // TODO: Change
};
AddExistingChannelView: {
teamId?: boolean;
};
InviteUsersEditView: {
rid: string;
};
MessagesView: {
rid: string;
t: RoomType;
name: string;
};
AutoTranslateView: {
rid: string;
room: IRoom;
};
DirectoryView: undefined;
QueueListView: undefined;
NotificationPrefView: {
rid: string;
room: IRoom;
};
ForwardLivechatView: {
rid: string;
};
CannedResponsesListView: {
rid: string;
};
CannedResponseDetail: {
cannedResponse: {
shortcut: string;
text: string;
scopeName: string;
tags: string[];
};
room: IRoom;
};
LivechatEditView: {
room: IRoom;
roomUser: any; // TODO: Change
};
PickerView: {
title: string;
data: []; // TODO: Change
value: any; // TODO: Change
onChangeText: TextInputProps['onChangeText'];
goBack: Function;
onChangeValue: Function;
};
ThreadMessagesView: {
rid: string;
t: RoomType;
};
TeamChannelsView: {
teamId: string;
};
MarkdownTableView: {
renderRows: Function;
tableWidth: number;
};
ReadReceiptsView: {
messageId: string;
};
SettingsView: undefined;
LanguageView: undefined;
ThemeView: undefined;
DefaultBrowserView: undefined;
ScreenLockConfigView: undefined;
StatusView: undefined;
ProfileView: undefined;
DisplayPrefsView: undefined;
AdminPanelView: undefined;
NewMessageView: undefined;
SelectedUsersViewCreateChannel: {
maxUsers: number;
showButton: boolean;
title: string;
buttonText: string;
nextAction: Function;
}; // TODO: Change
CreateChannelView: {
isTeam?: boolean; // TODO: To check
teamId?: string;
};
CreateDiscussionView: {
channel: IRoom;
message: IMessage;
showCloseModal: boolean;
};
E2ESaveYourPasswordView: undefined;
E2EHowItWorksView: {
showCloseModal: boolean;
};
E2EEnterYourPasswordView: undefined;
UserPreferencesView: undefined;
UserNotificationPrefView: undefined;
SecurityPrivacyView: undefined;
E2EEncryptionSecurityView: undefined;
};
export type MasterDetailInsideStackParamList = {
DrawerNavigator: NavigatorScreenParams<Partial<MasterDetailDrawerParamList>>; // TODO: Change
ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>;
AttachmentView: {
attachment: IAttachment;
};
ModalBlockView: {
data: any; // TODO: Change
};
JitsiMeetView: {
rid: string;
url: string;
onlyAudio?: boolean;
};
ShareView: {
attachments: IAttachment[];
isShareView?: boolean;
serverInfo: {};
text: string;
room: IRoom;
thread: any; // TODO: Change
};
};

View File

@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator, StackNavigationOptions } from '@react-navigation/stack';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ThemeContext } from '../theme'; import { ThemeContext } from '../theme';
import { ModalAnimation, StackAnimation, defaultHeader, themedHeader } from '../utils/navigation'; import { ModalAnimation, StackAnimation, defaultHeader, themedHeader } from '../utils/navigation';
// Outside Stack // Outside Stack
// import NewServerView from '../views/NewServerView'; // import NewServerView from '../views/NewServerView';
import WorkspaceView from '../views/WorkspaceView'; import WorkspaceView from '../views/WorkspaceView';
@ -14,37 +13,34 @@ import SendEmailConfirmationView from '../views/SendEmailConfirmationView';
import RegisterView from '../views/RegisterView'; import RegisterView from '../views/RegisterView';
import LegalView from '../views/LegalView'; import LegalView from '../views/LegalView';
import AuthenticationWebView from '../views/AuthenticationWebView'; import AuthenticationWebView from '../views/AuthenticationWebView';
import { OutsideModalParamList, OutsideParamList } from './types';
// Outside // Outside
const Outside = createStackNavigator(); const Outside = createStackNavigator<OutsideParamList>();
const _OutsideStack = () => { const _OutsideStack = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
<Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}> <Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
{/* <Outside.Screen name='NewServerView' component={NewServerView} options={NewServerView.navigationOptions} /> */} {/* <Outside.Screen name='NewServerView' component={NewServerView} options={NewServerView.navigationOptions} /> */}
<Outside.Screen name='WorkspaceView' component={WorkspaceView} options={WorkspaceView.navigationOptions} /> <Outside.Screen name='WorkspaceView' component={WorkspaceView} options={WorkspaceView.navigationOptions} />
<Outside.Screen name='LoginView' component={LoginView} options={LoginView.navigationOptions} /> <Outside.Screen name='LoginView' component={LoginView} options={LoginView.navigationOptions} />
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} /> <Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
<Outside.Screen <Outside.Screen name='SendEmailConfirmationView' component={SendEmailConfirmationView} />
name='SendEmailConfirmationView'
component={SendEmailConfirmationView}
options={SendEmailConfirmationView.navigationOptions}
/>
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} /> <Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
<Outside.Screen name='LegalView' component={LegalView} options={LegalView.navigationOptions} /> <Outside.Screen name='LegalView' component={LegalView} options={LegalView.navigationOptions} />
</Outside.Navigator> </Outside.Navigator>
); );
}; };
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
root: state.app.root root: state.app.root
}); });
const OutsideStack = connect(mapStateToProps)(_OutsideStack); const OutsideStack = connect(mapStateToProps)(_OutsideStack);
// OutsideStackModal // OutsideStackModal
const OutsideModal = createStackNavigator(); const OutsideModal = createStackNavigator<OutsideModalParamList>();
const OutsideStackModal = () => { const OutsideStackModal = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);

272
app/stacks/types.ts Normal file
View File

@ -0,0 +1,272 @@
import { NavigatorScreenParams } from '@react-navigation/core';
import { TextInputProps } from 'react-native';
import Model from '@nozbe/watermelondb/Model';
import { IOptionsField } from '../views/NotificationPreferencesView/options';
import { IServer } from '../definitions/IServer';
import { IAttachment } from '../definitions/IAttachment';
import { IMessage } from '../definitions/IMessage';
import { IRoom, RoomType } from '../definitions/IRoom';
export type ChatsStackParamList = {
RoomsListView: undefined;
RoomView: {
rid: string;
t: RoomType;
tmid?: string;
message?: string;
name?: string;
fname?: string;
prid?: string;
room: IRoom;
jumpToMessageId?: string;
jumpToThreadId?: string;
roomUserId?: string;
};
RoomActionsView: {
room: IRoom;
member: any;
rid: string;
t: RoomType;
joined: boolean;
};
SelectListView: {
data: any;
title: string;
infoText: string;
nextAction: Function;
showAlert: boolean;
isSearch: boolean;
onSearch: Function;
isRadio?: boolean;
};
RoomInfoView: {
room: IRoom;
member: any;
rid: string;
t: RoomType;
};
RoomInfoEditView: {
rid: string;
};
RoomMembersView: {
rid: string;
room: IRoom;
};
SearchMessagesView: {
rid: string;
t: RoomType;
encrypted?: boolean;
showCloseModal?: boolean;
};
SelectedUsersView: {
maxUsers?: number;
showButton?: boolean;
title?: string;
buttonText?: string;
nextAction?: Function;
};
InviteUsersView: {
rid: string;
};
InviteUsersEditView: {
rid: string;
};
MessagesView: {
rid: string;
t: RoomType;
name: string;
};
AutoTranslateView: {
rid: string;
room: IRoom;
};
DirectoryView: undefined;
NotificationPrefView: {
rid: string;
room: Model;
};
ForwardLivechatView: {
rid: string;
};
LivechatEditView: {
room: IRoom;
roomUser: any; // TODO: Change
};
PickerView: {
title: string;
data: IOptionsField[];
value?: any; // TODO: Change
onChangeText?: ((text: string) => IOptionsField[]) | ((term?: string) => Promise<any>);
goBack?: boolean;
onChangeValue: Function;
};
ThreadMessagesView: {
rid: string;
t: RoomType;
};
TeamChannelsView: {
teamId: string;
};
CreateChannelView: {
isTeam?: boolean; // TODO: To check
teamId?: string;
};
AddChannelTeamView: {
teamId?: string;
teamChannels: []; // TODO: Change
};
AddExistingChannelView: {
teamId?: string;
teamChannels: []; // TODO: Change
};
MarkdownTableView: {
renderRows: (drawExtraBorders?: boolean) => JSX.Element;
tableWidth: number;
};
ReadReceiptsView: {
messageId: string;
};
QueueListView: undefined;
CannedResponsesListView: {
rid: string;
};
CannedResponseDetail: {
cannedResponse: {
shortcut: string;
text: string;
scopeName: string;
tags: string[];
};
room: IRoom;
};
};
export type ProfileStackParamList = {
ProfileView: undefined;
UserPreferencesView: undefined;
UserNotificationPrefView: undefined;
PickerView: {
title: string;
data: IOptionsField[];
value: any; // TODO: Change
onChangeText?: TextInputProps['onChangeText'];
goBack?: Function;
onChangeValue: Function;
};
};
export type SettingsStackParamList = {
SettingsView: undefined;
SecurityPrivacyView: undefined;
E2EEncryptionSecurityView: undefined;
LanguageView: undefined;
ThemeView: undefined;
DefaultBrowserView: undefined;
ScreenLockConfigView: undefined;
ProfileView: undefined;
DisplayPrefsView: undefined;
};
export type AdminPanelStackParamList = {
AdminPanelView: undefined;
};
export type DisplayPrefStackParamList = {
DisplayPrefsView: undefined;
};
export type DrawerParamList = {
ChatsStackNavigator: NavigatorScreenParams<ChatsStackParamList>;
ProfileStackNavigator: NavigatorScreenParams<ProfileStackParamList>;
SettingsStackNavigator: NavigatorScreenParams<SettingsStackParamList>;
AdminPanelStackNavigator: NavigatorScreenParams<AdminPanelStackParamList>;
DisplayPrefStackNavigator: NavigatorScreenParams<DisplayPrefStackParamList>;
};
export type NewMessageStackParamList = {
NewMessageView: undefined;
SelectedUsersViewCreateChannel: {
maxUsers?: number;
showButton?: boolean;
title?: string;
buttonText?: string;
nextAction?: Function;
}; // TODO: Change
CreateChannelView: {
isTeam?: boolean; // TODO: To check
teamId?: string;
};
CreateDiscussionView: {
channel: IRoom;
message: IMessage;
showCloseModal: boolean;
};
};
export type E2ESaveYourPasswordStackParamList = {
E2ESaveYourPasswordView: undefined;
E2EHowItWorksView?: {
showCloseModal?: boolean;
};
};
export type E2EEnterYourPasswordStackParamList = {
E2EEnterYourPasswordView: undefined;
};
export type InsideStackParamList = {
DrawerNavigator: NavigatorScreenParams<DrawerParamList>;
NewMessageStackNavigator: NavigatorScreenParams<NewMessageStackParamList>;
E2ESaveYourPasswordStackNavigator: NavigatorScreenParams<E2ESaveYourPasswordStackParamList>;
E2EEnterYourPasswordStackNavigator: NavigatorScreenParams<E2EEnterYourPasswordStackParamList>;
AttachmentView: {
attachment: IAttachment;
};
StatusView: undefined;
ShareView: {
attachments: IAttachment[];
isShareView?: boolean;
isShareExtension: boolean;
serverInfo: IServer;
text: string;
room: IRoom;
thread: any; // TODO: Change
};
ModalBlockView: {
data: any; // TODO: Change;
};
JitsiMeetView: {
rid: string;
url: string;
onlyAudio?: boolean;
};
};
export type OutsideParamList = {
NewServerView: undefined;
WorkspaceView: undefined;
LoginView: {
title: string;
username?: string;
};
ForgotPasswordView: {
title: string;
};
SendEmailConfirmationView: {
user?: string;
};
RegisterView: {
title: string;
};
LegalView: undefined;
};
export type OutsideModalParamList = {
OutsideStack: NavigatorScreenParams<OutsideParamList>;
AuthenticationWebView: {
authType: string;
url: string;
ssoToken?: string;
};
};

View File

@ -12,7 +12,7 @@ interface IThemeContextProps {
export const ThemeContext = React.createContext<IThemeContextProps>({ theme: 'light' }); export const ThemeContext = React.createContext<IThemeContextProps>({ theme: 'light' });
export function withTheme(Component: any) { export function withTheme(Component: any): any {
const ThemedComponent = (props: any) => ( const ThemedComponent = (props: any) => (
<ThemeContext.Consumer>{contexts => <Component {...props} {...contexts} />}</ThemeContext.Consumer> <ThemeContext.Consumer>{contexts => <Component {...props} {...contexts} />}</ThemeContext.Consumer>
); );

View File

@ -5,13 +5,7 @@ import EventEmitter from '../events';
import { LISTENER } from '../../containers/Toast'; import { LISTENER } from '../../containers/Toast';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { DOCUMENTS_PATH, DOWNLOAD_PATH } from '../../constants/localPath'; import { DOCUMENTS_PATH, DOWNLOAD_PATH } from '../../constants/localPath';
import { IAttachment } from '../../definitions/IAttachment';
interface IAttachment {
title: string;
title_link: string;
type: string;
description: string;
}
export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`; export const getLocalFilePathFromFile = (localPath: string, attachment: IAttachment): string => `${localPath}${attachment.title}`;

View File

@ -253,7 +253,6 @@ export default {
RA_GO_AUTOTRANSLATE: 'ra_go_autotranslate', RA_GO_AUTOTRANSLATE: 'ra_go_autotranslate',
RA_GO_NOTIFICATIONPREF: 'ra_go_notification_pref', RA_GO_NOTIFICATIONPREF: 'ra_go_notification_pref',
RA_GO_FORWARDLIVECHAT: 'ra_go_forward_livechat', RA_GO_FORWARDLIVECHAT: 'ra_go_forward_livechat',
RA_GO_VISITORNAVIGATION: 'ra_go_visitor_navigation',
RA_SHARE: 'ra_share', RA_SHARE: 'ra_share',
RA_LEAVE: 'ra_leave', RA_LEAVE: 'ra_leave',
RA_LEAVE_F: 'ra_leave_f', RA_LEAVE_F: 'ra_leave_f',

View File

@ -1,10 +1,13 @@
export const canUploadFile = (file, allowList, maxFileSize) => { export const canUploadFile = (file, allowList, maxFileSize, permissionToUploadFile) => {
if (!(file && file.path)) { if (!(file && file.path)) {
return { success: true }; return { success: true };
} }
if (maxFileSize > -1 && file.size > maxFileSize) { if (maxFileSize > -1 && file.size > maxFileSize) {
return { success: false, error: 'error-file-too-large' }; return { success: false, error: 'error-file-too-large' };
} }
if (!permissionToUploadFile) {
return { success: false, error: 'error-not-permission-to-upload-file' };
}
// if white list is empty, all media types are enabled // if white list is empty, all media types are enabled
if (!allowList || allowList === '*') { if (!allowList || allowList === '*') {
return { success: true }; return { success: true };

View File

@ -7,6 +7,21 @@ import I18n from '../i18n';
import { extractHostname } from './server'; import { extractHostname } from './server';
const { SSLPinning } = NativeModules; const { SSLPinning } = NativeModules;
const { documentDirectory } = FileSystem;
const extractFileScheme = path => path.replace('file://', ''); // file:// isn't allowed by obj-C
const getPath = name => `${documentDirectory}/${name}`;
const persistCertificate = async (name, password) => {
const certificatePath = getPath(name);
const certificate = {
path: extractFileScheme(certificatePath),
password
};
await UserPreferences.setMapAsync(name, certificate);
return certificate;
};
const RCSSLPinning = Platform.select({ const RCSSLPinning = Platform.select({
ios: { ios: {
@ -25,17 +40,9 @@ const RCSSLPinning = Platform.select({
text: 'OK', text: 'OK',
onPress: async password => { onPress: async password => {
try { try {
const certificatePath = `${FileSystem.documentDirectory}/${name}`; const certificatePath = getPath(name);
await FileSystem.copyAsync({ from: uri, to: certificatePath }); await FileSystem.copyAsync({ from: uri, to: certificatePath });
await persistCertificate(name, password);
const certificate = {
path: certificatePath.replace('file://', ''), // file:// isn't allowed by obj-C
password
};
await UserPreferences.setMapAsync(name, certificate);
resolve(name); resolve(name);
} catch (e) { } catch (e) {
reject(e); reject(e);
@ -49,16 +56,19 @@ const RCSSLPinning = Platform.select({
reject(e); reject(e);
} }
}), }),
setCertificate: async (alias, server) => { setCertificate: async (name, server) => {
if (alias) { if (name) {
const certificate = await UserPreferences.getMapAsync(alias); let certificate = await UserPreferences.getMapAsync(name);
if (!certificate.path.match(extractFileScheme(documentDirectory))) {
certificate = await persistCertificate(name, certificate.password);
}
await UserPreferences.setMapAsync(extractHostname(server), certificate); await UserPreferences.setMapAsync(extractHostname(server), certificate);
} }
} }
}, },
android: { android: {
pickCertificate: () => SSLPinning?.pickCertificate(), pickCertificate: () => SSLPinning?.pickCertificate(),
setCertificate: alias => SSLPinning?.setCertificate(alias) setCertificate: name => SSLPinning?.setCertificate(name)
} }
}); });

View File

@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { CompositeNavigationProp } from '@react-navigation/core';
import * as List from '../containers/List'; import * as List from '../containers/List';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
@ -9,16 +10,24 @@ import { useTheme } from '../theme';
import * as HeaderButton from '../containers/HeaderButton'; import * as HeaderButton from '../containers/HeaderButton';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import I18n from '../i18n'; import I18n from '../i18n';
import { ChatsStackParamList, DrawerParamList, NewMessageStackParamList } from '../stacks/types';
type TNavigation = StackNavigationProp<any, 'AddChannelTeamView'>;
interface IAddChannelTeamView { interface IAddChannelTeamView {
route: RouteProp<{ AddChannelTeamView: { teamId: string; teamChannels: object[] } }, 'AddChannelTeamView'>; navigation: CompositeNavigationProp<
navigation: TNavigation; StackNavigationProp<ChatsStackParamList, 'AddChannelTeamView'>,
CompositeNavigationProp<StackNavigationProp<NewMessageStackParamList>, StackNavigationProp<DrawerParamList>>
>;
route: RouteProp<ChatsStackParamList, 'AddChannelTeamView'>;
isMasterDetail: boolean; isMasterDetail: boolean;
} }
const setHeader = (navigation: TNavigation, isMasterDetail: boolean) => { const setHeader = ({
navigation,
isMasterDetail
}: {
navigation: StackNavigationProp<ChatsStackParamList, 'AddChannelTeamView'>;
isMasterDetail: boolean;
}) => {
const options: StackNavigationOptions = { const options: StackNavigationOptions = {
headerTitle: I18n.t('Add_Channel_to_Team') headerTitle: I18n.t('Add_Channel_to_Team')
}; };
@ -35,7 +44,7 @@ const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTe
const { theme } = useTheme(); const { theme } = useTheme();
useEffect(() => { useEffect(() => {
setHeader(navigation, isMasterDetail); setHeader({ navigation, isMasterDetail });
}, []); }, []);
return ( return (

View File

@ -21,6 +21,7 @@ import { animateNextTransition } from '../utils/layoutAnimation';
import { goRoom } from '../utils/goRoom'; import { goRoom } from '../utils/goRoom';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import debounce from '../utils/debounce'; import debounce from '../utils/debounce';
import { ChatsStackParamList } from '../stacks/types';
interface IAddExistingChannelViewState { interface IAddExistingChannelViewState {
// TODO: refactor with Room Model // TODO: refactor with Room Model
@ -31,8 +32,8 @@ interface IAddExistingChannelViewState {
} }
interface IAddExistingChannelViewProps { interface IAddExistingChannelViewProps {
navigation: StackNavigationProp<any, 'AddExistingChannelView'>; navigation: StackNavigationProp<ChatsStackParamList, 'AddExistingChannelView'>;
route: RouteProp<{ AddExistingChannelView: { teamId: string } }, 'AddExistingChannelView'>; route: RouteProp<ChatsStackParamList, 'AddExistingChannelView'>;
theme: string; theme: string;
isMasterDetail: boolean; isMasterDetail: boolean;
addTeamChannelPermission: string[]; addTeamChannelPermission: string[];
@ -41,7 +42,7 @@ interface IAddExistingChannelViewProps {
const QUERY_SIZE = 50; const QUERY_SIZE = 50;
class AddExistingChannelView extends React.Component<IAddExistingChannelViewProps, IAddExistingChannelViewState> { class AddExistingChannelView extends React.Component<IAddExistingChannelViewProps, IAddExistingChannelViewState> {
private teamId: string; private teamId?: string;
constructor(props: IAddExistingChannelViewProps) { constructor(props: IAddExistingChannelViewProps) {
super(props); super(props);
this.query(); this.query();

View File

@ -9,6 +9,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { AdminPanelStackParamList } from '../../stacks/types';
interface IAdminPanelViewProps { interface IAdminPanelViewProps {
baseUrl: string; baseUrl: string;
@ -16,7 +17,7 @@ interface IAdminPanelViewProps {
} }
interface INavigationOptions { interface INavigationOptions {
navigation: DrawerScreenProps<any>; navigation: DrawerScreenProps<AdminPanelStackParamList, 'AdminPanelView'>;
isMasterDetail: boolean; isMasterDetail: boolean;
} }

View File

@ -24,6 +24,8 @@ import { getUserSelector } from '../selectors/login';
import { withDimensions } from '../dimensions'; import { withDimensions } from '../dimensions';
import { getHeaderHeight } from '../containers/Header'; import { getHeaderHeight } from '../containers/Header';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { InsideStackParamList } from '../stacks/types';
import { IAttachment } from '../definitions/IAttachment';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -31,24 +33,14 @@ const styles = StyleSheet.create({
} }
}); });
// TODO: refactor when react-navigation is done
export interface IAttachment {
title: string;
title_link?: string;
image_url?: string;
image_type?: string;
video_url?: string;
video_type?: string;
}
interface IAttachmentViewState { interface IAttachmentViewState {
attachment: IAttachment; attachment: IAttachment;
loading: boolean; loading: boolean;
} }
interface IAttachmentViewProps { interface IAttachmentViewProps {
navigation: StackNavigationProp<any, 'AttachmentView'>; navigation: StackNavigationProp<InsideStackParamList, 'AttachmentView'>;
route: RouteProp<{ AttachmentView: { attachment: IAttachment } }, 'AttachmentView'>; route: RouteProp<InsideStackParamList, 'AttachmentView'>;
theme: string; theme: string;
baseUrl: string; baseUrl: string;
width: number; width: number;

View File

@ -4,7 +4,9 @@ import { connect } from 'react-redux';
import parse from 'url-parse'; import parse from 'url-parse';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { WebViewMessage } from 'react-native-webview/lib/WebViewTypes'; import { WebViewMessage } from 'react-native-webview/lib/WebViewTypes';
import { RouteProp } from '@react-navigation/core';
import { OutsideModalParamList } from '../stacks/types';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { isIOS } from '../utils/deviceInfo'; import { isIOS } from '../utils/deviceInfo';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
@ -41,17 +43,9 @@ window.addEventListener('popstate', function() {
}); });
`; `;
interface IRoute {
params: {
authType: string;
url: string;
ssoToken?: string;
};
}
interface INavigationOption { interface INavigationOption {
navigation: StackNavigationProp<any, 'AuthenticationWebView'>; navigation: StackNavigationProp<OutsideModalParamList, 'AuthenticationWebView'>;
route: IRoute; route: RouteProp<OutsideModalParamList, 'AuthenticationWebView'>;
} }
interface IAuthenticationWebView extends INavigationOption { interface IAuthenticationWebView extends INavigationOption {

View File

@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import { FlatList, StyleSheet, Switch } from 'react-native'; import { FlatList, StyleSheet, Switch } from 'react-native';
import { RouteProp } from '@react-navigation/core';
import { ChatsStackParamList } from '../../stacks/types';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n'; import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
@ -9,6 +11,7 @@ import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { events, logEvent } from '../../utils/log'; import { events, logEvent } from '../../utils/log';
import { IRoom } from '../../definitions/IRoom';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
list: { list: {
@ -16,19 +19,8 @@ const styles = StyleSheet.create({
} }
}); });
interface IRoom {
observe: Function;
autoTranslateLanguage: boolean;
autoTranslate: boolean;
}
interface IAutoTranslateViewProps { interface IAutoTranslateViewProps {
route: { route: RouteProp<ChatsStackParamList, 'AutoTranslateView'>;
params: {
rid?: string;
room?: IRoom;
};
};
theme: string; theme: string;
} }

View File

@ -25,6 +25,7 @@ import { events, logEvent } from '../utils/log';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { ChatsStackParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -91,8 +92,8 @@ interface ICreateChannelViewState {
} }
interface ICreateChannelViewProps { interface ICreateChannelViewProps {
navigation: StackNavigationProp<any, 'CreateChannelView'>; navigation: StackNavigationProp<ChatsStackParamList, 'CreateChannelView'>;
route: RouteProp<{ CreateChannelView: { isTeam: boolean; teamId: string } }, 'CreateChannelView'>; route: RouteProp<ChatsStackParamList, 'CreateChannelView'>;
baseUrl: string; baseUrl: string;
create: (data: ICreateFunction) => void; create: (data: ICreateFunction) => void;
removeUser: (user: IOtherUser) => void; removeUser: (user: IOtherUser) => void;
@ -118,7 +119,7 @@ interface ISwitch extends SwitchProps {
} }
class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreateChannelViewState> { class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreateChannelViewState> {
private teamId: string; private teamId?: string;
constructor(props: ICreateChannelViewProps) { constructor(props: ICreateChannelViewProps) {
super(props); super(props);
@ -240,7 +241,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
broadcast, broadcast,
encrypted, encrypted,
isTeam, isTeam,
teamId: this.teamId teamId: this.teamId!
}); });
Review.pushPositiveEvent(); Review.pushPositiveEvent();

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ScrollView, Switch, Text } from 'react-native'; import { ScrollView, Switch, Text } from 'react-native';
import { StackNavigationOptions } from '@react-navigation/stack';
import Loading from '../../containers/Loading'; import Loading from '../../containers/Loading';
import KeyboardView from '../../presentation/KeyboardView'; import KeyboardView from '../../presentation/KeyboardView';
@ -89,7 +90,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, any> {
) )
: null, : null,
headerLeft: showCloseModal ? () => <HeaderButton.CloseModal navigation={navigation} /> : undefined headerLeft: showCloseModal ? () => <HeaderButton.CloseModal navigation={navigation} /> : undefined
}); } as StackNavigationOptions);
}; };
submit = () => { submit = () => {

View File

@ -1,14 +1,11 @@
import { RouteProp } from '@react-navigation/core';
import { StackNavigationProp } from '@react-navigation/stack';
import { NewMessageStackParamList } from '../../stacks/types';
export interface ICreateChannelViewProps { export interface ICreateChannelViewProps {
navigation: any; navigation: StackNavigationProp<NewMessageStackParamList, 'CreateDiscussionView'>;
route: { route: RouteProp<NewMessageStackParamList, 'CreateDiscussionView'>;
params?: {
channel: string;
message: {
msg: string;
};
showCloseModal: boolean;
};
};
server: string; server: string;
user: { user: {
id: string; id: string;

View File

@ -63,7 +63,11 @@ export default class DirectoryOptions extends PureComponent<IDirectoryOptionsPro
} }
return ( return (
<Touch onPress={() => changeType(itemType)} style={styles.dropdownItemButton} theme={theme}> <Touch
onPress={() => changeType(itemType)}
style={styles.dropdownItemButton}
theme={theme}
accessibilityLabel={I18n.t(text)}>
<View style={styles.dropdownItemContainer}> <View style={styles.dropdownItemContainer}>
<CustomIcon style={[styles.dropdownItemIcon, { color: themes[theme].bodyText }]} size={22} name={icon} /> <CustomIcon style={[styles.dropdownItemIcon, { color: themes[theme].bodyText }]} size={22} name={icon} />
<Text style={[styles.dropdownItemText, { color: themes[theme].bodyText }]}>{I18n.t(text)}</Text> <Text style={[styles.dropdownItemText, { color: themes[theme].bodyText }]}>{I18n.t(text)}</Text>
@ -90,7 +94,7 @@ export default class DirectoryOptions extends PureComponent<IDirectoryOptionsPro
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
<Animated.View <Animated.View
style={[styles.dropdownContainer, { transform: [{ translateY }], backgroundColor: themes[theme].backgroundColor }]}> style={[styles.dropdownContainer, { transform: [{ translateY }], backgroundColor: themes[theme].backgroundColor }]}>
<Touch onPress={this.close} theme={theme}> <Touch onPress={this.close} theme={theme} accessibilityLabel={I18n.t('Search_by')}>
<View <View
style={[ style={[
styles.dropdownContainerHeader, styles.dropdownContainerHeader,

View File

@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import { FlatList, Text, View } from 'react-native'; import { FlatList, Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { StackNavigationProp } from '@react-navigation/stack';
import { ChatsStackParamList } from '../../stacks/types';
import * as List from '../../containers/List'; import * as List from '../../containers/List';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
@ -24,7 +26,7 @@ import styles from './styles';
import Options from './Options'; import Options from './Options';
interface IDirectoryViewProps { interface IDirectoryViewProps {
navigation: object; navigation: StackNavigationProp<ChatsStackParamList, 'DirectoryView'>;
baseUrl: string; baseUrl: string;
isFederationEnabled: boolean; isFederationEnabled: boolean;
user: { user: {

View File

@ -17,6 +17,7 @@ import KeyboardView from '../presentation/KeyboardView';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { events, logEvent } from '../utils/log'; import { events, logEvent } from '../utils/log';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { E2EEnterYourPasswordStackParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -36,7 +37,7 @@ interface IE2EEnterYourPasswordViewState {
interface IE2EEnterYourPasswordViewProps { interface IE2EEnterYourPasswordViewProps {
encryptionDecodeKey: (password: string) => void; encryptionDecodeKey: (password: string) => void;
theme: string; theme: string;
navigation: StackNavigationProp<any, 'E2EEnterYourPasswordView'>; navigation: StackNavigationProp<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
} }
class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> { class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> {

View File

@ -9,6 +9,7 @@ import * as HeaderButton from '../containers/HeaderButton';
import Markdown from '../containers/markdown'; import Markdown from '../containers/markdown';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import I18n from '../i18n'; import I18n from '../i18n';
import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -23,8 +24,8 @@ const styles = StyleSheet.create({
}); });
interface INavigation { interface INavigation {
navigation: StackNavigationProp<any, 'E2EHowItWorksView'>; navigation: StackNavigationProp<E2ESaveYourPasswordStackParamList, 'E2EHowItWorksView'>;
route: RouteProp<{ E2EHowItWorksView: { showCloseModal: boolean } }, 'E2EHowItWorksView'>; route: RouteProp<E2ESaveYourPasswordStackParamList, 'E2EHowItWorksView'>;
} }
interface IE2EHowItWorksViewProps extends INavigation { interface IE2EHowItWorksViewProps extends INavigation {

View File

@ -19,6 +19,7 @@ import Button from '../containers/Button';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import I18n from '../i18n'; import I18n from '../i18n';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -60,7 +61,7 @@ interface IE2ESaveYourPasswordViewState {
interface IE2ESaveYourPasswordViewProps { interface IE2ESaveYourPasswordViewProps {
server: string; server: string;
navigation: StackNavigationProp<any, 'E2ESaveYourPasswordView'>; navigation: StackNavigationProp<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'>;
encryptionSetBanner(): void; encryptionSetBanner(): void;
theme: string; theme: string;
} }

View File

@ -14,6 +14,7 @@ import { themes } from '../constants/colors';
import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import { events, logEvent } from '../utils/log'; import { events, logEvent } from '../utils/log';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { OutsideParamList } from '../stacks/types';
interface IForgotPasswordViewState { interface IForgotPasswordViewState {
email: string; email: string;
@ -22,8 +23,8 @@ interface IForgotPasswordViewState {
} }
interface IForgotPasswordViewProps { interface IForgotPasswordViewProps {
navigation: StackNavigationProp<any, 'ForgotPasswordView'>; navigation: StackNavigationProp<OutsideParamList, 'ForgotPasswordView'>;
route: RouteProp<{ ForgotPasswordView: { title: string } }, 'ForgotPasswordView'>; route: RouteProp<OutsideParamList, 'ForgotPasswordView'>;
theme: string; theme: string;
} }

View File

@ -14,6 +14,7 @@ import OrSeparator from '../containers/OrSeparator';
import Input from '../containers/UIKit/MultiSelect/Input'; import Input from '../containers/UIKit/MultiSelect/Input';
import { forwardRoom as forwardRoomAction } from '../actions/room'; import { forwardRoom as forwardRoomAction } from '../actions/room';
import { ILivechatDepartment } from './definition/ILivechatDepartment'; import { ILivechatDepartment } from './definition/ILivechatDepartment';
import { ChatsStackParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -47,8 +48,8 @@ interface IParsedData {
} }
interface IForwardLivechatViewProps { interface IForwardLivechatViewProps {
navigation: StackNavigationProp<any, 'ForwardLivechatView'>; navigation: StackNavigationProp<ChatsStackParamList, 'ForwardLivechatView'>;
route: RouteProp<{ ForwardLivechatView: { rid: string } }, 'ForwardLivechatView'>; route: RouteProp<ChatsStackParamList, 'ForwardLivechatView'>;
theme: string; theme: string;
forwardRoom: (rid: string, transferData: ITransferData) => void; forwardRoom: (rid: string, transferData: ITransferData) => void;
} }

View File

@ -19,6 +19,7 @@ import { withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import { events, logEvent } from '../../utils/log'; import { events, logEvent } from '../../utils/log';
import styles from './styles'; import styles from './styles';
import { ChatsStackParamList } from '../../stacks/types';
const OPTIONS = { const OPTIONS = {
days: [ days: [
@ -67,9 +68,9 @@ const OPTIONS = {
] ]
}; };
interface IInviteUsersEditView { interface IInviteUsersEditViewProps {
navigation: StackNavigationProp<any, 'InviteUsersEditView'>; navigation: StackNavigationProp<ChatsStackParamList, 'InviteUsersEditView'>;
route: RouteProp<{ InviteUsersEditView: { rid: string } }, 'InviteUsersEditView'>; route: RouteProp<ChatsStackParamList, 'InviteUsersEditView'>;
theme: string; theme: string;
createInviteLink(rid: string): void; createInviteLink(rid: string): void;
inviteLinksSetParams(params: { [key: string]: number }): void; inviteLinksSetParams(params: { [key: string]: number }): void;
@ -77,14 +78,14 @@ interface IInviteUsersEditView {
maxUses: number; maxUses: number;
} }
class InviteUsersView extends React.Component<IInviteUsersEditView, any> { class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any> {
static navigationOptions = (): StackNavigationOptions => ({ static navigationOptions = (): StackNavigationOptions => ({
title: I18n.t('Invite_users') title: I18n.t('Invite_users')
}); });
private rid: string; private rid: string;
constructor(props: IInviteUsersEditView) { constructor(props: IInviteUsersEditViewProps) {
super(props); super(props);
this.rid = props.route.params?.rid; this.rid = props.route.params?.rid;
} }
@ -160,4 +161,4 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
createInviteLink: (rid: string) => dispatch(inviteLinksCreateAction(rid)) createInviteLink: (rid: string) => dispatch(inviteLinksCreateAction(rid))
}); });
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(InviteUsersView)); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(InviteUsersEditView));

View File

@ -6,6 +6,7 @@ import { StackNavigationProp, StackNavigationOptions } from '@react-navigation/s
import { RouteProp } from '@react-navigation/core'; import { RouteProp } from '@react-navigation/core';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { ChatsStackParamList } from '../../stacks/types';
import { import {
inviteLinksClear as inviteLinksClearAction, inviteLinksClear as inviteLinksClearAction,
inviteLinksCreate as inviteLinksCreateAction inviteLinksCreate as inviteLinksCreateAction
@ -22,9 +23,9 @@ import SafeAreaView from '../../containers/SafeAreaView';
import { events, logEvent } from '../../utils/log'; import { events, logEvent } from '../../utils/log';
import styles from './styles'; import styles from './styles';
interface IInviteUsersView { interface IInviteUsersViewProps {
navigation: StackNavigationProp<any, 'InviteUsersView'>; navigation: StackNavigationProp<ChatsStackParamList, 'InviteUsersView'>;
route: RouteProp<any, 'InviteUsersView'>; route: RouteProp<ChatsStackParamList, 'InviteUsersView'>;
theme: string; theme: string;
timeDateFormat: string; timeDateFormat: string;
invite: { invite: {
@ -36,14 +37,14 @@ interface IInviteUsersView {
createInviteLink(rid: string): void; createInviteLink(rid: string): void;
clearInviteLink(): void; clearInviteLink(): void;
} }
class InviteUsersView extends React.Component<IInviteUsersView, any> { class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
private rid: string; private rid: string;
static navigationOptions: StackNavigationOptions = { static navigationOptions: StackNavigationOptions = {
title: I18n.t('Invite_users') title: I18n.t('Invite_users')
}; };
constructor(props: IInviteUsersView) { constructor(props: IInviteUsersViewProps) {
super(props); super(props);
this.rid = props.route.params?.rid; this.rid = props.route.params?.rid;
} }

View File

@ -12,6 +12,7 @@ import ActivityIndicator from '../containers/ActivityIndicator';
import { events, logEvent } from '../utils/log'; import { events, logEvent } from '../utils/log';
import { isAndroid, isIOS } from '../utils/deviceInfo'; import { isAndroid, isIOS } from '../utils/deviceInfo';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { InsideStackParamList } from '../stacks/types';
const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) => const formatUrl = (url: string, baseUrl: string, uriSize: number, avatarAuthURLFragment: string) =>
`${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`; `${baseUrl}/avatar/${url}?format=png&width=${uriSize}&height=${uriSize}${avatarAuthURLFragment}`;
@ -25,8 +26,8 @@ interface IJitsiMeetViewState {
} }
interface IJitsiMeetViewProps { interface IJitsiMeetViewProps {
navigation: StackNavigationProp<any, 'JitsiMeetView'>; navigation: StackNavigationProp<InsideStackParamList, 'JitsiMeetView'>;
route: RouteProp<{ JitsiMeetView: { rid: string; url: string; onlyAudio?: boolean } }, 'JitsiMeetView'>; route: RouteProp<InsideStackParamList, 'JitsiMeetView'>;
baseUrl: string; baseUrl: string;
theme: string; theme: string;
user: { user: {

View File

@ -15,6 +15,7 @@ import TextInput from '../containers/TextInput';
import { loginRequest as loginRequestAction } from '../actions/login'; import { loginRequest as loginRequestAction } from '../actions/login';
import LoginServices from '../containers/LoginServices'; import LoginServices from '../containers/LoginServices';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { OutsideParamList } from '../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
registerDisabled: { registerDisabled: {
@ -47,9 +48,9 @@ const styles = StyleSheet.create({
} }
}); });
interface IProps { interface ILoginViewProps {
navigation: StackNavigationProp<any>; navigation: StackNavigationProp<OutsideParamList, 'LoginView'>;
route: RouteProp<any, 'RegisterView'>; route: RouteProp<OutsideParamList, 'LoginView'>;
Site_Name: string; Site_Name: string;
Accounts_RegistrationForm: string; Accounts_RegistrationForm: string;
Accounts_RegistrationForm_LinkReplacementText: string; Accounts_RegistrationForm_LinkReplacementText: string;
@ -67,15 +68,15 @@ interface IProps {
inviteLinkToken: string; inviteLinkToken: string;
} }
class LoginView extends React.Component<IProps, any> { class LoginView extends React.Component<ILoginViewProps, any> {
private passwordInput: any; private passwordInput: any;
static navigationOptions = ({ route, navigation }: Partial<IProps>) => ({ static navigationOptions = ({ route, navigation }: ILoginViewProps) => ({
title: route?.params?.title ?? 'Rocket.Chat', title: route?.params?.title ?? 'Rocket.Chat',
headerRight: () => <HeaderButton.Legal testID='login-view-more' navigation={navigation} /> headerRight: () => <HeaderButton.Legal testID='login-view-more' navigation={navigation} />
}); });
constructor(props: IProps) { constructor(props: ILoginViewProps) {
super(props); super(props);
this.state = { this.state = {
user: props.route.params?.username ?? '', user: props.route.params?.username ?? '',
@ -83,7 +84,7 @@ class LoginView extends React.Component<IProps, any> {
}; };
} }
UNSAFE_componentWillReceiveProps(nextProps: IProps) { UNSAFE_componentWillReceiveProps(nextProps: ILoginViewProps) {
const { error } = this.props; const { error } = this.props;
if (nextProps.failure && !dequal(error, nextProps.error)) { if (nextProps.failure && !dequal(error, nextProps.error)) {
if (nextProps.error?.error === 'error-invalid-email') { if (nextProps.error?.error === 'error-invalid-email') {

View File

@ -7,12 +7,10 @@ import I18n from '../i18n';
import { isIOS } from '../utils/deviceInfo'; import { isIOS } from '../utils/deviceInfo';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { ChatsStackParamList } from '../stacks/types';
interface IMarkdownTableViewProps { interface IMarkdownTableViewProps {
route: RouteProp< route: RouteProp<ChatsStackParamList, 'MarkdownTableView'>;
{ MarkdownTableView: { renderRows: (drawExtraBorders?: boolean) => JSX.Element; tableWidth: number } },
'MarkdownTableView'
>;
theme: string; theme: string;
} }

View File

@ -3,8 +3,9 @@ import { FlatList, Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core'; import { CompositeNavigationProp, RouteProp } from '@react-navigation/core';
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
import Message from '../../containers/message'; import Message from '../../containers/message';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -18,22 +19,19 @@ import { withActionSheet } from '../../containers/ActionSheet';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import getThreadName from '../../lib/methods/getThreadName'; import getThreadName from '../../lib/methods/getThreadName';
import styles from './styles'; import styles from './styles';
import { ChatsStackParamList } from '../../stacks/types';
type TMessagesViewRouteParams = { import { IRoom, RoomType } from '../../definitions/IRoom';
MessagesView: {
rid: string;
t: string;
name: string;
};
};
interface IMessagesViewProps { interface IMessagesViewProps {
user: { user: {
id: string; id: string;
}; };
baseUrl: string; baseUrl: string;
navigation: StackNavigationProp<any, 'MessagesView'>; navigation: CompositeNavigationProp<
route: RouteProp<TMessagesViewRouteParams, 'MessagesView'>; StackNavigationProp<ChatsStackParamList, 'MessagesView'>,
StackNavigationProp<MasterDetailInsideStackParamList>
>;
route: RouteProp<ChatsStackParamList, 'MessagesView'>;
customEmojis: { [key: string]: string }; customEmojis: { [key: string]: string };
theme: string; theme: string;
showActionSheet: Function; showActionSheet: Function;
@ -41,6 +39,14 @@ interface IMessagesViewProps {
isMasterDetail: boolean; isMasterDetail: boolean;
} }
interface IRoomInfoParam {
room: IRoom;
member: any;
rid: string;
t: RoomType;
joined: boolean;
}
interface IMessagesViewState { interface IMessagesViewState {
loading: boolean; loading: boolean;
messages: []; messages: [];
@ -65,17 +71,22 @@ interface IMessageItem {
} }
interface IParams { interface IParams {
rid?: string; rid: string;
jumpToMessageId: string; t: RoomType;
t?: string;
room: any;
tmid?: string; tmid?: string;
message?: string;
name?: string; name?: string;
fname?: string;
prid?: string;
room: IRoom;
jumpToMessageId?: string;
jumpToThreadId?: string;
roomUserId?: string;
} }
class MessagesView extends React.Component<IMessagesViewProps, any> { class MessagesView extends React.Component<IMessagesViewProps, any> {
private rid?: string; private rid: string;
private t?: string; private t: RoomType;
private content: any; private content: any;
private room: any; private room: any;
@ -121,7 +132,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
}); });
}; };
navToRoomInfo = (navParam: { rid: string }) => { navToRoomInfo = (navParam: IRoomInfoParam) => {
const { navigation, user } = this.props; const { navigation, user } = this.props;
if (navParam.rid === user.id) { if (navParam.rid === user.id) {
return; return;
@ -147,7 +158,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
...params, ...params,
tmid: item.tmid, tmid: item.tmid,
name: await getThreadName(this.rid, item.tmid, item._id), name: await getThreadName(this.rid, item.tmid, item._id),
t: 'thread' t: RoomType.THREAD
}; };
navigation.push('RoomView', params); navigation.push('RoomView', params);
} else { } else {

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view'; import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
@ -15,6 +16,7 @@ import { CONTAINER_TYPES, MODAL_ACTIONS } from '../lib/methods/actions';
import { textParser } from '../containers/UIKit/utils'; import { textParser } from '../containers/UIKit/utils';
import Navigation from '../lib/Navigation'; import Navigation from '../lib/Navigation';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { MasterDetailInsideStackParamList } from '../stacks/MasterDetailStack/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -30,14 +32,49 @@ const styles = StyleSheet.create({
} }
}); });
Object.fromEntries = Object.fromEntries || (arr => arr.reduce((acc, [k, v]) => ((acc[k] = v), acc), {})); interface IValueBlockId {
const groupStateByBlockIdMap = (obj, [key, { blockId, value }]) => { value: string;
blockId: string;
}
type TElementToState = [string, IValueBlockId];
interface IActions {
actionId: string;
value: any;
blockId?: string;
}
interface IValues {
[key: string]: {
[key: string]: string;
};
}
interface IModalBlockViewState {
data: any;
loading: boolean;
errors?: any;
}
interface IModalBlockViewProps {
navigation: StackNavigationProp<MasterDetailInsideStackParamList, 'ModalBlockView'>;
route: RouteProp<MasterDetailInsideStackParamList, 'ModalBlockView'>;
theme: string;
language: string;
user: {
id: string;
token: string;
};
}
// eslint-disable-next-line no-sequences
Object.fromEntries = Object.fromEntries || ((arr: any[]) => arr.reduce((acc, [k, v]) => ((acc[k] = v), acc), {}));
const groupStateByBlockIdMap = (obj: any, [key, { blockId, value }]: TElementToState) => {
obj[blockId] = obj[blockId] || {}; obj[blockId] = obj[blockId] || {};
obj[blockId][key] = value; obj[blockId][key] = value;
return obj; return obj;
}; };
const groupStateByBlockId = obj => Object.entries(obj).reduce(groupStateByBlockIdMap, {}); const groupStateByBlockId = (obj: { [key: string]: any }) => Object.entries(obj).reduce(groupStateByBlockIdMap, {});
const filterInputFields = ({ element, elements = [] }) => { const filterInputFields = ({ element, elements = [] }: { element: any; elements?: any[] }) => {
if (element && element.initialValue) { if (element && element.initialValue) {
return true; return true;
} }
@ -45,7 +82,8 @@ const filterInputFields = ({ element, elements = [] }) => {
return true; return true;
} }
}; };
const mapElementToState = ({ element, blockId, elements = [] }) => {
const mapElementToState = ({ element, blockId, elements = [] }: { element: any; blockId: string; elements?: any[] }): any => {
if (elements.length) { if (elements.length) {
return elements return elements
.map(e => ({ element: e, blockId })) .map(e => ({ element: e, blockId }))
@ -54,10 +92,15 @@ const mapElementToState = ({ element, blockId, elements = [] }) => {
} }
return [element.actionId, { value: element.initialValue, blockId }]; return [element.actionId, { value: element.initialValue, blockId }];
}; };
const reduceState = (obj, el) => (Array.isArray(el[0]) ? { ...obj, ...Object.fromEntries(el) } : { ...obj, [el[0]]: el[1] }); const reduceState = (obj: any, el: any) =>
Array.isArray(el[0]) ? { ...obj, ...Object.fromEntries(el) } : { ...obj, [el[0]]: el[1] };
class ModalBlockView extends React.Component { class ModalBlockView extends React.Component<IModalBlockViewProps, IModalBlockViewState> {
static navigationOptions = ({ route }) => { private submitting: boolean;
private values: IValues;
static navigationOptions = ({ route }: Pick<IModalBlockViewProps, 'route'>): StackNavigationOptions => {
const data = route.params?.data; const data = route.params?.data;
const { view } = data; const { view } = data;
const { title } = view; const { title } = view;
@ -66,18 +109,7 @@ class ModalBlockView extends React.Component {
}; };
}; };
static propTypes = { constructor(props: IModalBlockViewProps) {
navigation: PropTypes.object,
route: PropTypes.object,
theme: PropTypes.string,
language: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
})
};
constructor(props) {
super(props); super(props);
this.submitting = false; this.submitting = false;
const data = props.route.params?.data; const data = props.route.params?.data;
@ -95,7 +127,7 @@ class ModalBlockView extends React.Component {
EventEmitter.addEventListener(viewId, this.handleUpdate); EventEmitter.addEventListener(viewId, this.handleUpdate);
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps: IModalBlockViewProps) {
const { navigation, route } = this.props; const { navigation, route } = this.props;
const oldData = prevProps.route.params?.data ?? {}; const oldData = prevProps.route.params?.data ?? {};
const newData = route.params?.data ?? {}; const newData = route.params?.data ?? {};
@ -128,7 +160,7 @@ class ModalBlockView extends React.Component {
/> />
</HeaderButton.Container> </HeaderButton.Container>
) )
: null, : undefined,
headerRight: submit headerRight: submit
? () => ( ? () => (
<HeaderButton.Container> <HeaderButton.Container>
@ -140,13 +172,13 @@ class ModalBlockView extends React.Component {
/> />
</HeaderButton.Container> </HeaderButton.Container>
) )
: null : undefined
}); });
}; };
handleUpdate = ({ type, ...data }) => { handleUpdate = ({ type, ...data }: { type: string }) => {
if ([MODAL_ACTIONS.ERRORS].includes(type)) { if ([MODAL_ACTIONS.ERRORS].includes(type)) {
const { errors } = data; const { errors }: any = data;
this.setState({ errors }); this.setState({ errors });
} else { } else {
this.setState({ data }); this.setState({ data });
@ -154,7 +186,7 @@ class ModalBlockView extends React.Component {
} }
}; };
cancel = async ({ closeModal }) => { cancel = async ({ closeModal }: { closeModal?: () => void }) => {
const { data } = this.state; const { data } = this.state;
const { appId, viewId, view } = data; const { appId, viewId, view } = data;
@ -210,7 +242,7 @@ class ModalBlockView extends React.Component {
this.setState({ loading: false }); this.setState({ loading: false });
}; };
action = async ({ actionId, value, blockId }) => { action = async ({ actionId, value, blockId }: IActions) => {
const { data } = this.state; const { data } = this.state;
const { mid, appId, viewId } = data; const { mid, appId, viewId } = data;
await RocketChat.triggerBlockAction({ await RocketChat.triggerBlockAction({
@ -227,7 +259,7 @@ class ModalBlockView extends React.Component {
this.changeState({ actionId, value, blockId }); this.changeState({ actionId, value, blockId });
}; };
changeState = ({ actionId, value, blockId = 'default' }) => { changeState = ({ actionId, value, blockId = 'default' }: IActions) => {
this.values[actionId] = { this.values[actionId] = {
blockId, blockId,
value value
@ -266,7 +298,7 @@ class ModalBlockView extends React.Component {
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
language: state.login.user && state.login.user.language language: state.login.user && state.login.user.language
}); });

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { FlatList, StyleSheet, Text, View } from 'react-native'; import { FlatList, StyleSheet, Text, View } from 'react-native';
import { Dispatch } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
@ -18,7 +19,6 @@ import * as HeaderButton from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
import { getUserSelector } from '../selectors/login';
import Navigation from '../lib/Navigation'; import Navigation from '../lib/Navigation';
import { createChannelRequest } from '../actions/createChannel'; import { createChannelRequest } from '../actions/createChannel';
import { goRoom } from '../utils/goRoom'; import { goRoom } from '../utils/goRoom';
@ -47,33 +47,54 @@ const styles = StyleSheet.create({
} }
}); });
class NewMessageView extends React.Component { interface IButton {
static navigationOptions = ({ navigation }) => ({ onPress: () => void;
testID: string;
title: string;
icon: string;
first?: boolean;
}
interface ISearch {
_id: string;
status: string;
username: string;
avatarETag: string;
outside: boolean;
rid: string;
name: string;
t: string;
search: boolean;
}
interface INewMessageViewState {
search: ISearch[];
// TODO: Refactor when migrate room
chats: any[];
permissions: boolean[];
}
interface INewMessageViewProps {
navigation: StackNavigationProp<any, 'NewMessageView'>;
create: (params: { group: boolean }) => void;
maxUsers: number;
theme: string;
isMasterDetail: boolean;
serverVersion: string;
createTeamPermission: string[];
createDirectMessagePermission: string[];
createPublicChannelPermission: string[];
createPrivateChannelPermission: string[];
createDiscussionPermission: string[];
}
class NewMessageView extends React.Component<INewMessageViewProps, INewMessageViewState> {
static navigationOptions = ({ navigation }: INewMessageViewProps): StackNavigationOptions => ({
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='new-message-view-close' />, headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='new-message-view-close' />,
title: I18n.t('New_Message') title: I18n.t('New_Message')
}); });
static propTypes = { constructor(props: INewMessageViewProps) {
navigation: PropTypes.object,
baseUrl: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string,
roles: PropTypes.array
}),
create: PropTypes.func,
maxUsers: PropTypes.number,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
serverVersion: PropTypes.string,
createTeamPermission: PropTypes.array,
createDirectMessagePermission: PropTypes.array,
createPublicChannelPermission: PropTypes.array,
createPrivateChannelPermission: PropTypes.array,
createDiscussionPermission: PropTypes.array
};
constructor(props) {
super(props); super(props);
this.init(); this.init();
this.state = { this.state = {
@ -102,7 +123,7 @@ class NewMessageView extends React.Component {
this.handleHasPermission(); this.handleHasPermission();
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps: INewMessageViewProps) {
const { const {
createTeamPermission, createTeamPermission,
createPublicChannelPermission, createPublicChannelPermission,
@ -122,7 +143,7 @@ class NewMessageView extends React.Component {
} }
} }
onSearchChangeText(text) { onSearchChangeText(text: string) {
this.search(text); this.search(text);
} }
@ -131,7 +152,7 @@ class NewMessageView extends React.Component {
return navigation.pop(); return navigation.pop();
}; };
search = async text => { search = async (text: string) => {
const result = await RocketChat.search({ text, filterRooms: false }); const result = await RocketChat.search({ text, filterRooms: false });
this.setState({ this.setState({
search: result search: result
@ -162,7 +183,8 @@ class NewMessageView extends React.Component {
}); });
}; };
goRoom = item => { // TODO: Refactor when migrate room
goRoom = (item: any) => {
logEvent(events.NEW_MSG_CHAT_WITH_USER); logEvent(events.NEW_MSG_CHAT_WITH_USER);
const { isMasterDetail, navigation } = this.props; const { isMasterDetail, navigation } = this.props;
if (isMasterDetail) { if (isMasterDetail) {
@ -171,7 +193,7 @@ class NewMessageView extends React.Component {
goRoom({ item, isMasterDetail }); goRoom({ item, isMasterDetail });
}; };
renderButton = ({ onPress, testID, title, icon, first }) => { renderButton = ({ onPress, testID, title, icon, first }: IButton) => {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<Touch onPress={onPress} style={{ backgroundColor: themes[theme].backgroundColor }} testID={testID} theme={theme}> <Touch onPress={onPress} style={{ backgroundColor: themes[theme].backgroundColor }} testID={testID} theme={theme}>
@ -218,7 +240,7 @@ class NewMessageView extends React.Component {
return ( return (
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}> <View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='new-message-view-search' /> <SearchBox onChangeText={(text: string) => this.onSearchChangeText(text)} testID='new-message-view-search' />
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
{permissions[0] || permissions[1] {permissions[0] || permissions[1]
? this.renderButton({ ? this.renderButton({
@ -258,9 +280,10 @@ class NewMessageView extends React.Component {
); );
}; };
renderItem = ({ item, index }) => { // TODO: Refactor when migrate room
renderItem = ({ item, index }: { item: ISearch | any; index: number }) => {
const { search, chats } = this.state; const { search, chats } = this.state;
const { baseUrl, user, theme } = this.props; const { theme } = this.props;
let style = { borderColor: themes[theme].separatorColor }; let style = { borderColor: themes[theme].separatorColor };
if (index === 0) { if (index === 0) {
@ -277,10 +300,8 @@ class NewMessageView extends React.Component {
name={item.search ? item.name : item.fname} name={item.search ? item.name : item.fname}
username={item.search ? item.username : item.name} username={item.search ? item.username : item.name}
onPress={() => this.goRoom(item)} onPress={() => this.goRoom(item)}
baseUrl={baseUrl}
testID={`new-message-view-item-${item.name}`} testID={`new-message-view-item-${item.name}`}
style={style} style={style}
user={user}
theme={theme} theme={theme}
/> />
); );
@ -313,12 +334,10 @@ class NewMessageView extends React.Component {
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
serverVersion: state.server.version, serverVersion: state.server.version,
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
baseUrl: state.server.server,
maxUsers: state.settings.DirectMesssage_maxUsers || 1, maxUsers: state.settings.DirectMesssage_maxUsers || 1,
user: getUserSelector(state),
createTeamPermission: state.permissions['create-team'], createTeamPermission: state.permissions['create-team'],
createDirectMessagePermission: state.permissions['create-d'], createDirectMessagePermission: state.permissions['create-d'],
createPublicChannelPermission: state.permissions['create-c'], createPublicChannelPermission: state.permissions['create-c'],
@ -326,8 +345,8 @@ const mapStateToProps = state => ({
createDiscussionPermission: state.permissions['start-discussion'] createDiscussionPermission: state.permissions['start-discussion']
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = (dispatch: Dispatch) => ({
create: params => dispatch(createChannelRequest(params)) create: (params: { group: boolean }) => dispatch(createChannelRequest(params))
}); });
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView)); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));

View File

@ -33,6 +33,7 @@ import { isTablet } from '../../utils/deviceInfo';
import { verticalScale, moderateScale } from '../../utils/scaling'; import { verticalScale, moderateScale } from '../../utils/scaling';
import { withDimensions } from '../../dimensions'; import { withDimensions } from '../../dimensions';
import ServerInput from './ServerInput'; import ServerInput from './ServerInput';
import { OutsideParamList } from '../../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
onboardingImage: { onboardingImage: {
@ -73,7 +74,7 @@ export interface IServer extends Model {
} }
interface INewServerView { interface INewServerView {
navigation: StackNavigationProp<any, 'NewServerView'>; navigation: StackNavigationProp<OutsideParamList, 'NewServerView'>;
theme: string; theme: string;
connecting: boolean; connecting: boolean;
connectServer(server: string, username?: string, fromServerHistory?: boolean): void; connectServer(server: string, username?: string, fromServerHistory?: boolean): void;

View File

@ -17,6 +17,7 @@ import SafeAreaView from '../../containers/SafeAreaView';
import log, { events, logEvent } from '../../utils/log'; import log, { events, logEvent } from '../../utils/log';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import { OPTIONS } from './options'; import { OPTIONS } from './options';
import { ChatsStackParamList } from '../../stacks/types';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pickerText: { pickerText: {
@ -26,16 +27,8 @@ const styles = StyleSheet.create({
}); });
interface INotificationPreferencesView { interface INotificationPreferencesView {
navigation: StackNavigationProp<any, 'NotificationPreferencesView'>; navigation: StackNavigationProp<ChatsStackParamList, 'NotificationPrefView'>;
route: RouteProp< route: RouteProp<ChatsStackParamList, 'NotificationPrefView'>;
{
NotificationPreferencesView: {
rid: string;
room: Model;
};
},
'NotificationPreferencesView'
>;
theme: string; theme: string;
} }

View File

@ -1,4 +1,4 @@
interface IOptionsField { export interface IOptionsField {
label: string; label: string;
value: string | number; value: string | number;
second?: number; second?: number;

View File

@ -11,6 +11,8 @@ import * as List from '../containers/List';
import SearchBox from '../containers/SearchBox'; import SearchBox from '../containers/SearchBox';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { ChatsStackParamList } from '../stacks/types';
import { IOptionsField } from './NotificationPreferencesView/options';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
search: { search: {
@ -25,37 +27,21 @@ const styles = StyleSheet.create({
} }
}); });
interface IData {
label: string;
value: string;
second?: string;
}
interface IItem { interface IItem {
item: IData; item: IOptionsField;
selected: boolean; selected: boolean;
onItemPress: () => void; onItemPress: () => void;
theme: string; theme: string;
} }
interface IPickerViewState { interface IPickerViewState {
data: IData[]; data: IOptionsField[];
value: string; value: string;
} }
interface IParams {
title: string;
value: string;
data: IData[];
onChangeText: (value: string) => IData[];
goBack: boolean;
onChange: Function;
onChangeValue: (value: string) => void;
}
interface IPickerViewProps { interface IPickerViewProps {
navigation: StackNavigationProp<any, 'PickerView'>; navigation: StackNavigationProp<ChatsStackParamList, 'PickerView'>;
route: RouteProp<{ PickerView: IParams }, 'PickerView'>; route: RouteProp<ChatsStackParamList, 'PickerView'>;
theme: string; theme: string;
} }
@ -69,7 +55,7 @@ const Item = React.memo(({ item, selected, onItemPress, theme }: IItem) => (
)); ));
class PickerView extends React.PureComponent<IPickerViewProps, IPickerViewState> { class PickerView extends React.PureComponent<IPickerViewProps, IPickerViewState> {
private onSearch: (text: string) => IData[]; private onSearch?: ((text: string) => IOptionsField[]) | ((term?: string | undefined) => Promise<any>);
static navigationOptions = ({ route }: IPickerViewProps) => ({ static navigationOptions = ({ route }: IPickerViewProps) => ({
title: route.params?.title ?? I18n.t('Select_an_option') title: route.params?.title ?? I18n.t('Select_an_option')
@ -126,13 +112,13 @@ class PickerView extends React.PureComponent<IPickerViewProps, IPickerViewState>
{this.renderSearch()} {this.renderSearch()}
<FlatList <FlatList
data={data} data={data}
keyExtractor={item => item.value} keyExtractor={item => item.value as string}
renderItem={({ item }) => ( renderItem={({ item }) => (
<Item <Item
item={item} item={item}
theme={theme} theme={theme}
selected={!this.onSearch && (value || data[0]?.value) === item.value} selected={!this.onSearch && (value || data[0]?.value) === item.value}
onItemPress={() => this.onChangeValue(item.value)} onItemPress={() => this.onChangeValue(item.value as string)}
/> />
)} )}
ItemSeparatorComponent={List.Separator} ItemSeparatorComponent={List.Separator}

View File

@ -1,6 +1,8 @@
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import React from 'react'; import React from 'react';
import { ProfileStackParamList } from '../../stacks/types';
export interface IUser { export interface IUser {
id: string; id: string;
name: string; name: string;
@ -31,14 +33,12 @@ export interface IAvatarButton {
} }
export interface INavigationOptions { export interface INavigationOptions {
navigation: StackNavigationProp<any, 'ProfileView'>; navigation: StackNavigationProp<ProfileStackParamList, 'ProfileView'>;
isMasterDetail?: boolean; isMasterDetail?: boolean;
} }
export interface IProfileViewProps { export interface IProfileViewProps {
user: IUser; user: IUser;
navigation: StackNavigationProp<any, 'ProfileView'>;
isMasterDetail?: boolean;
baseUrl: string; baseUrl: string;
Accounts_AllowEmailChange: boolean; Accounts_AllowEmailChange: boolean;
Accounts_AllowPasswordChange: boolean; Accounts_AllowPasswordChange: boolean;

View File

@ -16,6 +16,7 @@ import { withTheme } from '../../theme';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import styles from './styles'; import styles from './styles';
import { ChatsStackParamList } from '../../stacks/types';
interface IReceipts { interface IReceipts {
_id: string; _id: string;
@ -36,8 +37,8 @@ interface IReadReceiptViewState {
} }
interface INavigationOption { interface INavigationOption {
navigation: StackNavigationProp<any, 'ReadReceiptView'>; navigation: StackNavigationProp<ChatsStackParamList, 'ReadReceiptsView'>;
route: RouteProp<{ ReadReceiptView: { messageId: string } }, 'ReadReceiptView'>; route: RouteProp<ChatsStackParamList, 'ReadReceiptsView'>;
isMasterDetail: boolean; isMasterDetail: boolean;
} }

View File

@ -5,6 +5,7 @@ import { RouteProp } from '@react-navigation/core';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import { OutsideParamList } from '../stacks/types';
import log, { events, logEvent } from '../utils/log'; import log, { events, logEvent } from '../utils/log';
import Button from '../containers/Button'; import Button from '../containers/Button';
import I18n from '../i18n'; import I18n from '../i18n';
@ -51,8 +52,8 @@ const styles = StyleSheet.create({
}); });
interface IProps { interface IProps {
navigation: StackNavigationProp<any>; navigation: StackNavigationProp<OutsideParamList, 'RegisterView'>;
route: RouteProp<any, 'RegisterView'>; route: RouteProp<OutsideParamList, 'RegisterView'>;
server: string; server: string;
Site_Name: string; Site_Name: string;
Gitlab_URL: string; Gitlab_URL: string;

View File

@ -918,7 +918,6 @@ class RoomActionsView extends React.Component {
event: this.convertTeamToChannel event: this.convertTeamToChannel
}) })
} }
testID='room-actions-convert-channel-to-team'
left={() => <List.Icon name='channel-public' />} left={() => <List.Icon name='channel-public' />}
showActionIndicator showActionIndicator
/> />
@ -1198,23 +1197,6 @@ class RoomActionsView extends React.Component {
<List.Separator /> <List.Separator />
</> </>
) : null} ) : null}
{['l'].includes(t) && !this.isOmnichannelPreview ? (
<>
<List.Item
title='Navigation_history'
onPress={() =>
this.onPressTouchable({
route: 'VisitorNavigationView',
params: { rid }
})
}
left={() => <List.Icon name='history' />}
showActionIndicator
/>
<List.Separator />
</>
) : null}
</List.Section> </List.Section>
{this.renderLastSection()} {this.renderLastSection()}

View File

@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { Switch } from 'react-native'; import { Switch } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { StackNavigationOptions } from '@react-navigation/stack';
import Model from '@nozbe/watermelondb/Model';
import { Subscription } from 'rxjs';
import I18n from '../i18n'; import I18n from '../i18n';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
@ -16,19 +18,42 @@ import { events, logEvent } from '../utils/log';
const DEFAULT_BIOMETRY = false; const DEFAULT_BIOMETRY = false;
class ScreenLockConfigView extends React.Component { interface IServerRecords extends Model {
static navigationOptions = () => ({ autoLock?: boolean;
autoLockTime?: number;
biometry?: boolean;
}
interface IItem {
title: string;
value: number;
disabled?: boolean;
}
interface IScreenLockConfigViewProps {
theme: string;
server: string;
Force_Screen_Lock: boolean;
Force_Screen_Lock_After: number;
}
interface IScreenLockConfigViewState {
autoLock?: boolean;
autoLockTime?: number | null;
biometry?: boolean;
biometryLabel: null;
}
class ScreenLockConfigView extends React.Component<IScreenLockConfigViewProps, IScreenLockConfigViewState> {
private serverRecord?: IServerRecords;
private observable?: Subscription;
static navigationOptions = (): StackNavigationOptions => ({
title: I18n.t('Screen_lock') title: I18n.t('Screen_lock')
}); });
static propTypes = { constructor(props: IScreenLockConfigViewProps) {
theme: PropTypes.string,
server: PropTypes.string,
Force_Screen_Lock: PropTypes.string,
Force_Screen_Lock_After: PropTypes.string
};
constructor(props) {
super(props); super(props);
this.state = { this.state = {
autoLock: false, autoLock: false,
@ -104,7 +129,7 @@ class ScreenLockConfigView extends React.Component {
logEvent(events.SLC_SAVE_SCREEN_LOCK); logEvent(events.SLC_SAVE_SCREEN_LOCK);
const { autoLock, autoLockTime, biometry } = this.state; const { autoLock, autoLockTime, biometry } = this.state;
const serversDB = database.servers; const serversDB = database.servers;
await serversDB.action(async () => { await serversDB.write(async () => {
await this.serverRecord?.update(record => { await this.serverRecord?.update(record => {
record.autoLock = autoLock; record.autoLock = autoLock;
record.autoLockTime = autoLockTime === null ? DEFAULT_AUTO_LOCK : autoLockTime; record.autoLockTime = autoLockTime === null ? DEFAULT_AUTO_LOCK : autoLockTime;
@ -113,7 +138,7 @@ class ScreenLockConfigView extends React.Component {
}); });
}; };
changePasscode = async ({ force }) => { changePasscode = async ({ force }: { force: boolean }) => {
logEvent(events.SLC_CHANGE_PASSCODE); logEvent(events.SLC_CHANGE_PASSCODE);
await changePasscode({ force }); await changePasscode({ force });
}; };
@ -144,12 +169,12 @@ class ScreenLockConfigView extends React.Component {
); );
}; };
isSelected = value => { isSelected = (value: number) => {
const { autoLockTime } = this.state; const { autoLockTime } = this.state;
return autoLockTime === value; return autoLockTime === value;
}; };
changeAutoLockTime = autoLockTime => { changeAutoLockTime = (autoLockTime: number) => {
logEvent(events.SLC_CHANGE_AUTOLOCK_TIME); logEvent(events.SLC_CHANGE_AUTOLOCK_TIME);
this.setState({ autoLockTime }, () => this.save()); this.setState({ autoLockTime }, () => this.save());
}; };
@ -159,7 +184,7 @@ class ScreenLockConfigView extends React.Component {
return <List.Icon name='check' color={themes[theme].tintColor} />; return <List.Icon name='check' color={themes[theme].tintColor} />;
}; };
renderItem = ({ item }) => { renderItem = ({ item }: { item: IItem }) => {
const { title, value, disabled } = item; const { title, value, disabled } = item;
return ( return (
<> <>
@ -194,7 +219,7 @@ class ScreenLockConfigView extends React.Component {
if (!autoLock) { if (!autoLock) {
return null; return null;
} }
let items = this.defaultAutoLockOptions; let items: IItem[] = this.defaultAutoLockOptions;
if (Force_Screen_Lock && Force_Screen_Lock_After > 0) { if (Force_Screen_Lock && Force_Screen_Lock_After > 0) {
items = [ items = [
{ {
@ -262,7 +287,7 @@ class ScreenLockConfigView extends React.Component {
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
server: state.server.server, server: state.server.server,
Force_Screen_Lock: state.settings.Force_Screen_Lock, Force_Screen_Lock: state.settings.Force_Screen_Lock,
Force_Screen_Lock_After: state.settings.Force_Screen_Lock_After Force_Screen_Lock_After: state.settings.Force_Screen_Lock_After

View File

@ -1,19 +1,25 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
import useDeepCompareEffect from 'use-deep-compare-effect'; import useDeepCompareEffect from 'use-deep-compare-effect';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import { withTheme } from '../theme'; import { useTheme } from '../theme';
import EventEmitter from '../utils/events'; import EventEmitter from '../utils/events';
import { LOCAL_AUTHENTICATE_EMITTER } from '../constants/localAuthentication'; import { LOCAL_AUTHENTICATE_EMITTER } from '../constants/localAuthentication';
import { isTablet } from '../utils/deviceInfo'; import { isTablet } from '../utils/deviceInfo';
import { PasscodeEnter } from '../containers/Passcode'; import { PasscodeEnter } from '../containers/Passcode';
const ScreenLockedView = ({ theme }) => { interface IData {
submit?: () => void;
hasBiometry?: boolean;
}
const ScreenLockedView = (): JSX.Element => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [data, setData] = useState({}); const [data, setData] = useState<IData>({});
const { theme } = useTheme();
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
if (!isEmpty(data)) { if (!isEmpty(data)) {
@ -23,7 +29,7 @@ const ScreenLockedView = ({ theme }) => {
} }
}, [data]); }, [data]);
const showScreenLock = args => { const showScreenLock = (args: IData) => {
setData(args); setData(args);
}; };
@ -56,13 +62,9 @@ const ScreenLockedView = ({ theme }) => {
style={{ margin: 0 }} style={{ margin: 0 }}
animationIn='fadeIn' animationIn='fadeIn'
animationOut='fadeOut'> animationOut='fadeOut'>
<PasscodeEnter theme={theme} hasBiometry={data?.hasBiometry} finishProcess={onSubmit} /> <PasscodeEnter theme={theme} hasBiometry={!!data?.hasBiometry} finishProcess={onSubmit} />
</Modal> </Modal>
); );
}; };
ScreenLockedView.propTypes = { export default ScreenLockedView;
theme: PropTypes.string
};
export default withTheme(ScreenLockedView);

View File

@ -1,11 +1,13 @@
import React from 'react'; import React from 'react';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core'; import { CompositeNavigationProp, RouteProp } from '@react-navigation/core';
import { FlatList, Text, View } from 'react-native'; import { FlatList, Text, View } from 'react-native';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import { IRoom, RoomType } from '../../definitions/IRoom';
import { IAttachment } from '../../definitions/IAttachment';
import RCTextInput from '../../containers/TextInput'; import RCTextInput from '../../containers/TextInput';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
import Markdown from '../../containers/markdown'; import Markdown from '../../containers/markdown';
@ -13,7 +15,7 @@ import debounce from '../../utils/debounce';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import Message from '../../containers/message'; import Message from '../../containers/message';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { IMessage, IMessageAttachments } from '../../containers/message/interfaces'; import { IMessage } from '../../containers/message/interfaces';
import I18n from '../../i18n'; import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import log from '../../utils/log'; import log from '../../utils/log';
@ -29,26 +31,30 @@ import getRoomInfo from '../../lib/methods/getRoomInfo';
import { isIOS } from '../../utils/deviceInfo'; import { isIOS } from '../../utils/deviceInfo';
import { compareServerVersion, methods } from '../../lib/utils'; import { compareServerVersion, methods } from '../../lib/utils';
import styles from './styles'; import styles from './styles';
import { InsideStackParamList, ChatsStackParamList } from '../../stacks/types';
const QUERY_SIZE = 50; const QUERY_SIZE = 50;
type TRouteParams = {
SearchMessagesView: {
showCloseModal?: boolean;
rid: string;
t?: string;
encrypted?: boolean;
};
};
interface ISearchMessagesViewState { interface ISearchMessagesViewState {
loading: boolean; loading: boolean;
messages: IMessage[]; messages: IMessage[];
searchText: string; searchText: string;
} }
interface IRoomInfoParam {
room: IRoom;
member: any;
rid: string;
t: RoomType;
joined: boolean;
}
interface INavigationOption { interface INavigationOption {
navigation: StackNavigationProp<any, 'SearchMessagesView'>; navigation: CompositeNavigationProp<
route: RouteProp<TRouteParams, 'SearchMessagesView'>; StackNavigationProp<ChatsStackParamList, 'SearchMessagesView'>,
StackNavigationProp<InsideStackParamList>
>;
route: RouteProp<ChatsStackParamList, 'SearchMessagesView'>;
} }
interface ISearchMessagesViewProps extends INavigationOption { interface ISearchMessagesViewProps extends INavigationOption {
@ -183,12 +189,12 @@ class SearchMessagesView extends React.Component<ISearchMessagesViewProps, ISear
return null; return null;
}; };
showAttachment = (attachment: IMessageAttachments) => { showAttachment = (attachment: IAttachment) => {
const { navigation } = this.props; const { navigation } = this.props;
navigation.navigate('AttachmentView', { attachment }); navigation.navigate('AttachmentView', { attachment });
}; };
navToRoomInfo = (navParam: IMessage) => { navToRoomInfo = (navParam: IRoomInfoParam) => {
const { navigation, user } = this.props; const { navigation, user } = this.props;
if (navParam.rid === user.id) { if (navParam.rid === user.id) {
return; return;

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Switch } from 'react-native'; import { Switch } from 'react-native';
import PropTypes from 'prop-types'; import { StackNavigationProp } from '@react-navigation/stack';
import AsyncStorage from '@react-native-community/async-storage'; import AsyncStorage from '@react-native-community/async-storage';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
@ -20,11 +20,15 @@ import {
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import { isFDroidBuild } from '../constants/environment'; import { isFDroidBuild } from '../constants/environment';
const SecurityPrivacyView = ({ navigation }) => { interface ISecurityPrivacyViewProps {
navigation: StackNavigationProp<any, 'SecurityPrivacyView'>;
}
const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Element => {
const [crashReportState, setCrashReportState] = useState(getReportCrashErrorsValue()); const [crashReportState, setCrashReportState] = useState(getReportCrashErrorsValue());
const [analyticsEventsState, setAnalyticsEventsState] = useState(getReportAnalyticsEventsValue()); const [analyticsEventsState, setAnalyticsEventsState] = useState(getReportAnalyticsEventsValue());
const e2eEnabled = useSelector(state => state.settings.E2E_Enable); const e2eEnabled = useSelector((state: any) => state.settings.E2E_Enable);
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation.setOptions({
@ -32,21 +36,22 @@ const SecurityPrivacyView = ({ navigation }) => {
}); });
}, []); }, []);
const toggleCrashReport = value => { const toggleCrashReport = (value: boolean) => {
logEvent(events.SE_TOGGLE_CRASH_REPORT); logEvent(events.SP_TOGGLE_CRASH_REPORT);
AsyncStorage.setItem(CRASH_REPORT_KEY, JSON.stringify(value)); AsyncStorage.setItem(CRASH_REPORT_KEY, JSON.stringify(value));
setCrashReportState(value); setCrashReportState(value);
toggleCrashErrorsReport(value); toggleCrashErrorsReport(value);
}; };
const toggleAnalyticsEvents = value => { const toggleAnalyticsEvents = (value: boolean) => {
logEvent(events.SE_TOGGLE_ANALYTICS_EVENTS); logEvent(events.SP_TOGGLE_ANALYTICS_EVENTS);
AsyncStorage.setItem(ANALYTICS_EVENTS_KEY, JSON.stringify(value)); AsyncStorage.setItem(ANALYTICS_EVENTS_KEY, JSON.stringify(value));
setAnalyticsEventsState(value); setAnalyticsEventsState(value);
toggleAnalyticsEventsReport(value); toggleAnalyticsEventsReport(value);
}; };
const navigateToScreen = screen => { const navigateToScreen = (screen: 'E2EEncryptionSecurityView' | 'ScreenLockConfigView') => {
// @ts-ignore
logEvent(events[`SP_GO_${screen.replace('View', '').toUpperCase()}`]); logEvent(events[`SP_GO_${screen.replace('View', '').toUpperCase()}`]);
navigation.navigate(screen); navigation.navigate(screen);
}; };
@ -106,8 +111,4 @@ const SecurityPrivacyView = ({ navigation }) => {
); );
}; };
SecurityPrivacyView.propTypes = {
navigation: PropTypes.object
};
export default SecurityPrivacyView; export default SecurityPrivacyView;

View File

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { FlatList, StyleSheet, Text, View } from 'react-native'; import { FlatList, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { RadioButton } from 'react-native-ui-lib'; import { RadioButton } from 'react-native-ui-lib';
import { RouteProp } from '@react-navigation/native';
import log from '../utils/log'; import log from '../utils/log';
import * as List from '../containers/List'; import * as List from '../containers/List';
@ -25,15 +26,58 @@ const styles = StyleSheet.create({
} }
}); });
class SelectListView extends React.Component { interface IData {
static propTypes = { rid: string;
navigation: PropTypes.object, name: string;
route: PropTypes.object, t?: string;
theme: PropTypes.string, teamMain?: boolean;
isMasterDetail: PropTypes.bool alert?: boolean;
}; }
constructor(props) { interface ISelectListViewState {
data: IData[];
dataFiltered: IData[];
isSearching: boolean;
selected: string[];
}
interface ISelectListViewProps {
navigation: StackNavigationProp<any, 'SelectListView'>;
route: RouteProp<
{
SelectView: {
data: IData[];
title: string;
infoText: string;
nextAction(selected: string[]): void;
showAlert(): void;
isSearch: boolean;
onSearch(text: string): IData[];
isRadio: boolean;
};
},
'SelectView'
>;
theme: string;
isMasterDetail: boolean;
}
class SelectListView extends React.Component<ISelectListViewProps, ISelectListViewState> {
private title: string;
private infoText: string;
private nextAction: (selected: string[]) => void;
private showAlert: () => void;
private isSearch: boolean;
private onSearch: (text: string) => IData[];
private isRadio: boolean;
constructor(props: ISelectListViewProps) {
super(props); super(props);
const data = props.route?.params?.data; const data = props.route?.params?.data;
this.title = props.route?.params?.title; this.title = props.route?.params?.title;
@ -56,7 +100,7 @@ class SelectListView extends React.Component {
const { navigation, isMasterDetail } = this.props; const { navigation, isMasterDetail } = this.props;
const { selected } = this.state; const { selected } = this.state;
const options = { const options: StackNavigationOptions = {
headerTitle: I18n.t(this.title) headerTitle: I18n.t(this.title)
}; };
@ -87,7 +131,7 @@ class SelectListView extends React.Component {
return ( return (
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}> <View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
<SearchBox <SearchBox
onChangeText={text => this.search(text)} onChangeText={(text: string) => this.search(text)}
testID='select-list-view-search' testID='select-list-view-search'
onCancelPress={() => this.setState({ isSearching: false })} onCancelPress={() => this.setState({ isSearching: false })}
/> />
@ -95,7 +139,7 @@ class SelectListView extends React.Component {
); );
}; };
search = async text => { search = async (text: string) => {
try { try {
this.setState({ isSearching: true }); this.setState({ isSearching: true });
const result = await this.onSearch(text); const result = await this.onSearch(text);
@ -105,12 +149,12 @@ class SelectListView extends React.Component {
} }
}; };
isChecked = rid => { isChecked = (rid: string) => {
const { selected } = this.state; const { selected } = this.state;
return selected.includes(rid); return selected.includes(rid);
}; };
toggleItem = rid => { toggleItem = (rid: string) => {
const { selected } = this.state; const { selected } = this.state;
animateNextTransition(); animateNextTransition();
@ -126,7 +170,7 @@ class SelectListView extends React.Component {
} }
}; };
renderItem = ({ item }) => { renderItem = ({ item }: { item: IData }) => {
const { theme } = this.props; const { theme } = this.props;
const { selected } = this.state; const { selected } = this.state;
@ -187,7 +231,7 @@ class SelectListView extends React.Component {
} }
} }
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
isMasterDetail: state.app.isMasterDetail isMasterDetail: state.app.isMasterDetail
}); });

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import PropTypes from 'prop-types'; import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb'; import { Q, Model } from '@nozbe/watermelondb';
import I18n from '../i18n'; import I18n from '../i18n';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
@ -12,29 +12,39 @@ import database from '../lib/database';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import * as List from '../containers/List'; import * as List from '../containers/List';
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const getItemLayout = (data: any, index: number) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.id; const keyExtractor = (item: IServer) => item.id;
class SelectServerView extends React.Component { interface IServer extends Model {
static navigationOptions = () => ({ id: string;
iconURL?: string;
name?: string;
}
interface ISelectServerViewState {
servers: IServer[];
}
interface ISelectServerViewProps {
navigation: StackNavigationProp<any, 'SelectServerView'>;
server: string;
}
class SelectServerView extends React.Component<ISelectServerViewProps, ISelectServerViewState> {
static navigationOptions = (): StackNavigationOptions => ({
title: I18n.t('Select_Server') title: I18n.t('Select_Server')
}); });
static propTypes = { state = { servers: [] as IServer[] };
server: PropTypes.string,
navigation: PropTypes.object
};
state = { servers: [] };
async componentDidMount() { async componentDidMount() {
const serversDB = database.servers; const serversDB = database.servers;
const serversCollection = serversDB.get('servers'); const serversCollection = serversDB.get('servers');
const servers = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetch(); const servers: IServer[] = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetch();
this.setState({ servers }); this.setState({ servers });
} }
select = async server => { select = async (server: string) => {
const { server: currentServer, navigation } = this.props; const { server: currentServer, navigation } = this.props;
navigation.navigate('ShareListView'); navigation.navigate('ShareListView');
@ -43,7 +53,7 @@ class SelectServerView extends React.Component {
} }
}; };
renderItem = ({ item }) => { renderItem = ({ item }: { item: IServer }) => {
const { server } = this.props; const { server } = this.props;
return <ServerItem onPress={() => this.select(item.id)} item={item} hasCheck={item.id === server} />; return <ServerItem onPress={() => this.select(item.id)} item={item} hasCheck={item.id === server} />;
}; };
@ -62,7 +72,6 @@ class SelectServerView extends React.Component {
ItemSeparatorComponent={List.Separator} ItemSeparatorComponent={List.Separator}
ListHeaderComponent={List.Separator} ListHeaderComponent={List.Separator}
ListFooterComponent={List.Separator} ListFooterComponent={List.Separator}
enableEmptySections
removeClippedSubviews removeClippedSubviews
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
@ -71,7 +80,7 @@ class SelectServerView extends React.Component {
} }
} }
const mapStateToProps = ({ share }) => ({ const mapStateToProps = ({ share }: any) => ({
server: share.server.server server: share.server.server
}); });

View File

@ -1,9 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import { FlatList, View } from 'react-native'; import { FlatList, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import { Subscription } from 'rxjs';
import * as List from '../containers/List'; import * as List from '../containers/List';
import database from '../lib/database'; import database from '../lib/database';
@ -22,33 +24,51 @@ import { addUser as addUserAction, removeUser as removeUserAction, reset as rese
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { ChatsStackParamList } from '../stacks/types';
const ITEM_WIDTH = 250; const ITEM_WIDTH = 250;
const getItemLayout = (_, index) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index }); const getItemLayout = (_: any, index: number) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index });
class SelectedUsersView extends React.Component { interface IUser {
static propTypes = { _id: string;
baseUrl: PropTypes.string, name: string;
addUser: PropTypes.func.isRequired, fname: string;
removeUser: PropTypes.func.isRequired, search?: boolean;
reset: PropTypes.func.isRequired, // username is used when is from searching
users: PropTypes.array, username?: string;
loading: PropTypes.bool, }
user: PropTypes.shape({ interface ISelectedUsersViewState {
id: PropTypes.string, maxUsers?: number;
token: PropTypes.string, search: IUser[];
username: PropTypes.string, chats: IUser[];
name: PropTypes.string }
}),
navigation: PropTypes.object, interface ISelectedUsersViewProps {
route: PropTypes.object, navigation: StackNavigationProp<ChatsStackParamList, 'SelectedUsersView'>;
theme: PropTypes.string route: RouteProp<ChatsStackParamList, 'SelectedUsersView'>;
baseUrl: string;
addUser(user: IUser): void;
removeUser(user: IUser): void;
reset(): void;
users: IUser[];
loading: boolean;
user: {
id: string;
token: string;
username: string;
name: string;
}; };
theme: string;
}
constructor(props) { class SelectedUsersView extends React.Component<ISelectedUsersViewProps, ISelectedUsersViewState> {
private flatlist?: FlatList;
private querySubscription?: Subscription;
constructor(props: ISelectedUsersViewProps) {
super(props); super(props);
this.init(); this.init();
this.flatlist = React.createRef();
const maxUsers = props.route.params?.maxUsers; const maxUsers = props.route.params?.maxUsers;
this.state = { this.state = {
maxUsers, maxUsers,
@ -62,7 +82,7 @@ class SelectedUsersView extends React.Component {
this.setHeader(props.route.params?.showButton); this.setHeader(props.route.params?.showButton);
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps: ISelectedUsersViewProps) {
if (this.isGroupChat()) { if (this.isGroupChat()) {
const { users } = this.props; const { users } = this.props;
if (prevProps.users.length !== users.length) { if (prevProps.users.length !== users.length) {
@ -80,7 +100,7 @@ class SelectedUsersView extends React.Component {
} }
// showButton can be sent as route params or updated by the component // showButton can be sent as route params or updated by the component
setHeader = showButton => { setHeader = (showButton?: boolean) => {
const { navigation, route } = this.props; const { navigation, route } = this.props;
const title = route.params?.title ?? I18n.t('Select_Users'); const title = route.params?.title ?? I18n.t('Select_Users');
const buttonText = route.params?.buttonText ?? I18n.t('Next'); const buttonText = route.params?.buttonText ?? I18n.t('Next');
@ -107,7 +127,8 @@ class SelectedUsersView extends React.Component {
.query(Q.where('t', 'd')) .query(Q.where('t', 'd'))
.observeWithColumns(['room_updated_at']); .observeWithColumns(['room_updated_at']);
this.querySubscription = observable.subscribe(data => { // TODO: Refactor when migrate room
this.querySubscription = observable.subscribe((data: any) => {
const chats = orderBy(data, ['roomUpdatedAt'], ['desc']); const chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
this.setState({ chats }); this.setState({ chats });
}); });
@ -116,11 +137,11 @@ class SelectedUsersView extends React.Component {
} }
}; };
onSearchChangeText(text) { onSearchChangeText(text: string) {
this.search(text); this.search(text);
} }
search = async text => { search = async (text: string) => {
const result = await RocketChat.search({ text, filterRooms: false }); const result = await RocketChat.search({ text, filterRooms: false });
this.setState({ this.setState({
search: result search: result
@ -129,15 +150,15 @@ class SelectedUsersView extends React.Component {
isGroupChat = () => { isGroupChat = () => {
const { maxUsers } = this.state; const { maxUsers } = this.state;
return maxUsers > 2; return maxUsers! > 2;
}; };
isChecked = username => { isChecked = (username: string) => {
const { users } = this.props; const { users } = this.props;
return users.findIndex(el => el.name === username) !== -1; return users.findIndex(el => el.name === username) !== -1;
}; };
toggleUser = user => { toggleUser = (user: IUser) => {
const { maxUsers } = this.state; const { maxUsers } = this.state;
const { const {
addUser, addUser,
@ -163,29 +184,29 @@ class SelectedUsersView extends React.Component {
} }
}; };
_onPressItem = (id, item = {}) => { _onPressItem = (id: string, item = {} as IUser) => {
if (item.search) { if (item.search) {
this.toggleUser({ _id: item._id, name: item.username, fname: item.name }); this.toggleUser({ _id: item._id, name: item.username!, fname: item.name });
} else { } else {
this.toggleUser({ _id: item._id, name: item.name, fname: item.fname }); this.toggleUser({ _id: item._id, name: item.name, fname: item.fname });
} }
}; };
_onPressSelectedItem = item => this.toggleUser(item); _onPressSelectedItem = (item: IUser) => this.toggleUser(item);
renderHeader = () => { renderHeader = () => {
const { theme } = this.props; const { theme } = this.props;
return ( return (
<View style={{ backgroundColor: themes[theme].backgroundColor }}> <View style={{ backgroundColor: themes[theme].backgroundColor }}>
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='select-users-view-search' /> <SearchBox onChangeText={(text: string) => this.onSearchChangeText(text)} testID='select-users-view-search' />
{this.renderSelected()} {this.renderSelected()}
</View> </View>
); );
}; };
setFlatListRef = ref => (this.flatlist = ref); setFlatListRef = (ref: FlatList) => (this.flatlist = ref);
onContentSizeChange = () => this.flatlist.scrollToEnd({ animated: true }); onContentSizeChange = () => this.flatlist?.scrollToEnd({ animated: true });
renderSelected = () => { renderSelected = () => {
const { users, theme } = this.props; const { users, theme } = this.props;
@ -204,35 +225,32 @@ class SelectedUsersView extends React.Component {
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]} style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
contentContainerStyle={{ marginVertical: 5 }} contentContainerStyle={{ marginVertical: 5 }}
renderItem={this.renderSelectedItem} renderItem={this.renderSelectedItem}
enableEmptySections
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
horizontal horizontal
/> />
); );
}; };
renderSelectedItem = ({ item }) => { renderSelectedItem = ({ item }: { item: IUser }) => {
const { baseUrl, user, theme } = this.props; const { theme } = this.props;
return ( return (
<UserItem <UserItem
name={item.fname} name={item.fname}
username={item.name} username={item.name}
onPress={() => this._onPressSelectedItem(item)} onPress={() => this._onPressSelectedItem(item)}
testID={`selected-user-${item.name}`} testID={`selected-user-${item.name}`}
baseUrl={baseUrl}
style={{ paddingRight: 15 }} style={{ paddingRight: 15 }}
user={user}
theme={theme} theme={theme}
/> />
); );
}; };
renderItem = ({ item, index }) => { renderItem = ({ item, index }: { item: IUser; index: number }) => {
const { search, chats } = this.state; const { search, chats } = this.state;
const { baseUrl, user, theme } = this.props; const { theme } = this.props;
const name = item.search ? item.name : item.fname; const name = item.search ? item.name : item.fname;
const username = item.search ? item.username : item.name; const username = item.search ? item.username! : item.name;
let style = { borderColor: themes[theme].separatorColor }; let style = { borderColor: themes[theme].separatorColor };
if (index === 0) { if (index === 0) {
style = { ...style, ...sharedStyles.separatorTop }; style = { ...style, ...sharedStyles.separatorTop };
@ -250,9 +268,7 @@ class SelectedUsersView extends React.Component {
onPress={() => this._onPressItem(item._id, item)} onPress={() => this._onPressItem(item._id, item)}
testID={`select-users-view-item-${item.name}`} testID={`select-users-view-item-${item.name}`}
icon={this.isChecked(username) ? 'check' : null} icon={this.isChecked(username) ? 'check' : null}
baseUrl={baseUrl}
style={style} style={style}
user={user}
theme={theme} theme={theme}
/> />
); );
@ -275,7 +291,6 @@ class SelectedUsersView extends React.Component {
ItemSeparatorComponent={List.Separator} ItemSeparatorComponent={List.Separator}
ListHeaderComponent={this.renderHeader} ListHeaderComponent={this.renderHeader}
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }} contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
enableEmptySections
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
); );
@ -293,16 +308,16 @@ class SelectedUsersView extends React.Component {
}; };
} }
const mapStateToProps = state => ({ const mapStateToProps = (state: any) => ({
baseUrl: state.server.server, baseUrl: state.server.server,
users: state.selectedUsers.users, users: state.selectedUsers.users,
loading: state.selectedUsers.loading, loading: state.selectedUsers.loading,
user: getUserSelector(state) user: getUserSelector(state)
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = (dispatch: any) => ({
addUser: user => dispatch(addUserAction(user)), addUser: (user: any) => dispatch(addUserAction(user)),
removeUser: user => dispatch(removeUserAction(user)), removeUser: (user: any) => dispatch(removeUserAction(user)),
reset: () => dispatch(resetAction()) reset: () => dispatch(resetAction())
}); });

View File

@ -1,6 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { StackNavigationProp } from '@react-navigation/stack'; import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core';
import { OutsideParamList } from '../stacks/types';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import Button from '../containers/Button'; import Button from '../containers/Button';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
@ -12,16 +14,12 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import log, { events, logEvent } from '../utils/log'; import log, { events, logEvent } from '../utils/log';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
interface ISendEmailConfirmationView { interface ISendEmailConfirmationViewProps {
navigation: StackNavigationProp<any, 'SendEmailConfirmationView'>; navigation: StackNavigationProp<OutsideParamList, 'SendEmailConfirmationView'>;
route: { route: RouteProp<OutsideParamList, 'SendEmailConfirmationView'>;
params: {
user?: string;
};
};
} }
const SendEmailConfirmationView = ({ navigation, route }: ISendEmailConfirmationView): JSX.Element => { const SendEmailConfirmationView = ({ navigation, route }: ISendEmailConfirmationViewProps): JSX.Element => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [invalidEmail, setInvalidEmail] = useState(true); const [invalidEmail, setInvalidEmail] = useState(true);
const [isFetching, setIsFetching] = useState(false); const [isFetching, setIsFetching] = useState(false);

Some files were not shown because too many files have changed in this diff Show More