[CHORE] Update react-navigation to v5 (#2154)
* react-navigation v5 installed
* compiling
* Outside working
* InsideStack compiling
* Switch stack
* Starting room
* RoomView header
* SafeAreaView
* Slide from right stack animation
* stash
* Fix params
* Create channel
* inapp notification
* Custom status
* Add server working
* Refactor appStart
* Attachment
* in-app notification
* AuthLoadingView
* Remove compat
* Navigation
* Outside animations
* Fix new server icon
* block modal
* AttachmentView header
* Remove unnecessary code
* SelectedUsersView header
* StatusView
* CreateDiscussionView
* RoomInfoView
* RoomInfoEditView style
* RoomMembersView
* RoomsListView header
* RoomView header
* Share extension
* getParam
* Focus/blur
* Trying to fix inapp
* Lint
* Simpler app container
* Update libs
* Revert "Simpler app container"
This reverts commit 1e49d80bb4
.
* Load messages faster
* Fix safearea on ReactionsModal
* Update safe area to v3
* lint
* Fix transition
* stash - drawer replace working
* stash - modal nav
* RoomActionsView as tablet modal
* RoomStack
* Stop showing RoomView header when there's no room
* Custom Header and different navigation based on stack
* Refactor setHeader
* MasterDetailContext
* RoomView header
* Fix isMasterDetail rule
* KeyCommands kind of working
* Create channel on tablet
* RoomView sCU
* Remove withSplit
* Settings opening as modal
* Settings
* StatusView headerLeft
* Admin panel
* TwoFactor style
* DirectoryView
* ServerDropdown and SortDropdown animations
* ThreadMessagesView
* Navigate to empty RoomView on server switch when in master detail
* ProfileView header
* Fix navigation issues
* Nav to any room info on tablet
* Room info
* Refactoring
* Fix rooms search
* Roomslist commands
* SearchMessagesView close modal
* Key commands
* Fix undefined subscription
* Disallow navigate to focused room
* isFocused state on RoomsListView
* Blur text inputs when focus is lost
* Replace animation
* Default nav theme
* Refactoring
* Always open Attachment with close modal button
* ModalContainer backdrop following themes
* Screen tracking
* Refactor get active route for in-app notification
* Only mark room as focused when in master detail layout
* Lint
* Open modals as fade from bottom on Android
* typo
* Fixing tests
* Fix in-app update
* Fixing goRoom issues
* Refactor stack names
* Fix unreadsCount
* Fix stack
* Fix header animation
* Refactor ShareNavigation
* Refactor navigation theme
* Make sure title is set
* Fix create discussion navigation
* Remove unused variable
* Create discussions from actions fixed
* Layout animation
* Screen lock on share extension
* Unnecessary change
* Admin border
* Set header after state callback
* Fix key commands on outside stack
* Fix back button pressed
* Remove layout animations from Android
* Tweak animations on Android
* Disable swipe gesture to open drawer
* Fix current item on RoomsListView
* Fix add server
* Fix drawer
* Fix broadcast
* LayoutAnimation instead of Transitions
* Fix onboarding back press
* Fix assorted tests
* Create discussion fix
* RoomInfoView header
* Drawer active item
This commit is contained in:
parent
5dcf2212e0
commit
98ed84ba5c
|
@ -0,0 +1,117 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
|
import Navigation from './lib/Navigation';
|
||||||
|
import { defaultHeader, getActiveRouteName, navigationTheme } from './utils/navigation';
|
||||||
|
import {
|
||||||
|
ROOT_LOADING, ROOT_OUTSIDE, ROOT_NEW_SERVER, ROOT_INSIDE, ROOT_SET_USERNAME, ROOT_BACKGROUND
|
||||||
|
} from './actions/app';
|
||||||
|
|
||||||
|
// Stacks
|
||||||
|
import AuthLoadingView from './views/AuthLoadingView';
|
||||||
|
|
||||||
|
// SetUsername Stack
|
||||||
|
import SetUsernameView from './views/SetUsernameView';
|
||||||
|
|
||||||
|
import OutsideStack from './stacks/OutsideStack';
|
||||||
|
import InsideStack from './stacks/InsideStack';
|
||||||
|
import MasterDetailStack from './stacks/MasterDetailStack';
|
||||||
|
import { ThemeContext } from './theme';
|
||||||
|
import { setCurrentScreen } from './utils/log';
|
||||||
|
|
||||||
|
// SetUsernameStack
|
||||||
|
const SetUsername = createStackNavigator();
|
||||||
|
const SetUsernameStack = () => (
|
||||||
|
<SetUsername.Navigator screenOptions={defaultHeader}>
|
||||||
|
<SetUsername.Screen
|
||||||
|
name='SetUsernameView'
|
||||||
|
component={SetUsernameView}
|
||||||
|
/>
|
||||||
|
</SetUsername.Navigator>
|
||||||
|
);
|
||||||
|
|
||||||
|
// App
|
||||||
|
const Stack = createStackNavigator();
|
||||||
|
const App = React.memo(({ root, isMasterDetail }) => {
|
||||||
|
if (!root) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
const navTheme = navigationTheme(theme);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const state = Navigation.navigationRef.current.getRootState();
|
||||||
|
const currentRouteName = getActiveRouteName(state);
|
||||||
|
Navigation.routeNameRef.current = currentRouteName;
|
||||||
|
setCurrentScreen(currentRouteName);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
|
||||||
|
<NavigationContainer
|
||||||
|
theme={navTheme}
|
||||||
|
ref={Navigation.navigationRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
const previousRouteName = Navigation.routeNameRef.current;
|
||||||
|
const currentRouteName = getActiveRouteName(state);
|
||||||
|
if (previousRouteName !== currentRouteName) {
|
||||||
|
setCurrentScreen(currentRouteName);
|
||||||
|
}
|
||||||
|
Navigation.routeNameRef.current = currentRouteName;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Navigator screenOptions={{ headerShown: false, animationEnabled: false }}>
|
||||||
|
<>
|
||||||
|
{root === ROOT_LOADING || root === ROOT_BACKGROUND ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='AuthLoading'
|
||||||
|
component={AuthLoadingView}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === ROOT_OUTSIDE || root === ROOT_NEW_SERVER ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='OutsideStack'
|
||||||
|
component={OutsideStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === ROOT_INSIDE && isMasterDetail ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='MasterDetailStack'
|
||||||
|
component={MasterDetailStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === ROOT_INSIDE && !isMasterDetail ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='InsideStack'
|
||||||
|
component={InsideStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === ROOT_SET_USERNAME ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='SetUsernameStack'
|
||||||
|
component={SetUsernameStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
</Stack.Navigator>
|
||||||
|
</NavigationContainer>
|
||||||
|
</SafeAreaProvider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
root: state.app.root,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
|
});
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
root: PropTypes.string,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppContainer = connect(mapStateToProps)(App);
|
||||||
|
export default AppContainer;
|
|
@ -33,7 +33,7 @@ export const ROOMS = createRequestTypes('ROOMS', [
|
||||||
'CLOSE_SEARCH_HEADER'
|
'CLOSE_SEARCH_HEADER'
|
||||||
]);
|
]);
|
||||||
export const ROOM = createRequestTypes('ROOM', ['SUBSCRIBE', 'UNSUBSCRIBE', 'LEAVE', 'DELETE', 'REMOVED', 'CLOSE', 'FORWARD', 'USER_TYPING']);
|
export const ROOM = createRequestTypes('ROOM', ['SUBSCRIBE', 'UNSUBSCRIBE', 'LEAVE', 'DELETE', 'REMOVED', 'CLOSE', 'FORWARD', 'USER_TYPING']);
|
||||||
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT', 'INIT_LOCAL_SETTINGS']);
|
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT', 'INIT_LOCAL_SETTINGS', 'SET_MASTER_DETAIL']);
|
||||||
export const MESSAGES = createRequestTypes('MESSAGES', ['REPLY_BROADCAST']);
|
export const MESSAGES = createRequestTypes('MESSAGES', ['REPLY_BROADCAST']);
|
||||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes]);
|
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes]);
|
||||||
export const CREATE_DISCUSSION = createRequestTypes('CREATE_DISCUSSION', [...defaultTypes]);
|
export const CREATE_DISCUSSION = createRequestTypes('CREATE_DISCUSSION', [...defaultTypes]);
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { APP } from './actionsTypes';
|
||||||
|
|
||||||
|
export const ROOT_OUTSIDE = 'outside';
|
||||||
|
export const ROOT_INSIDE = 'inside';
|
||||||
|
export const ROOT_LOADING = 'loading';
|
||||||
|
export const ROOT_NEW_SERVER = 'newServer';
|
||||||
|
export const ROOT_SET_USERNAME = 'setUsername';
|
||||||
|
export const ROOT_BACKGROUND = 'background';
|
||||||
|
|
||||||
|
export function appStart({ root, ...args }) {
|
||||||
|
return {
|
||||||
|
type: APP.START,
|
||||||
|
root,
|
||||||
|
...args
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appReady() {
|
||||||
|
return {
|
||||||
|
type: APP.READY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appInit() {
|
||||||
|
return {
|
||||||
|
type: APP.INIT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appInitLocalSettings() {
|
||||||
|
return {
|
||||||
|
type: APP.INIT_LOCAL_SETTINGS
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setMasterDetail(isMasterDetail) {
|
||||||
|
return {
|
||||||
|
type: APP.SET_MASTER_DETAIL,
|
||||||
|
isMasterDetail
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
import * as types from '../constants/types';
|
|
||||||
import { APP } from './actionsTypes';
|
|
||||||
|
|
||||||
export function appStart(root, text) {
|
|
||||||
return {
|
|
||||||
type: APP.START,
|
|
||||||
root,
|
|
||||||
text
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appReady() {
|
|
||||||
return {
|
|
||||||
type: APP.READY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appInit() {
|
|
||||||
return {
|
|
||||||
type: APP.INIT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appInitLocalSettings() {
|
|
||||||
return {
|
|
||||||
type: APP.INIT_LOCAL_SETTINGS
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setCurrentServer(server) {
|
|
||||||
return {
|
|
||||||
type: types.SET_CURRENT_SERVER,
|
|
||||||
payload: server
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function login() {
|
|
||||||
return {
|
|
||||||
type: 'LOGIN'
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -44,9 +44,10 @@ export function serverFailure(err) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serverInitAdd() {
|
export function serverInitAdd(previousServer) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.INIT_ADD
|
type: SERVER.INIT_ADD,
|
||||||
|
previousServer
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable no-bitwise */
|
/* eslint-disable no-bitwise */
|
||||||
import { constants } from 'react-native-keycommands';
|
import KeyCommands, { constants } from 'react-native-keycommands';
|
||||||
|
|
||||||
import I18n from './i18n';
|
import I18n from './i18n';
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ const KEY_ADD_SERVER = __DEV__ ? 'l' : 'n';
|
||||||
const KEY_SEND_MESSAGE = '\r';
|
const KEY_SEND_MESSAGE = '\r';
|
||||||
const KEY_SELECT = '123456789';
|
const KEY_SELECT = '123456789';
|
||||||
|
|
||||||
export const defaultCommands = [
|
const keyCommands = [
|
||||||
{
|
{
|
||||||
// Focus messageBox
|
// Focus messageBox
|
||||||
input: KEY_TYPING,
|
input: KEY_TYPING,
|
||||||
|
@ -29,10 +29,7 @@ export const defaultCommands = [
|
||||||
input: KEY_SEND_MESSAGE,
|
input: KEY_SEND_MESSAGE,
|
||||||
modifierFlags: 0,
|
modifierFlags: 0,
|
||||||
discoverabilityTitle: I18n.t('Send')
|
discoverabilityTitle: I18n.t('Send')
|
||||||
}
|
},
|
||||||
];
|
|
||||||
|
|
||||||
export const keyCommands = [
|
|
||||||
{
|
{
|
||||||
// Open Preferences Modal
|
// Open Preferences Modal
|
||||||
input: KEY_PREFERENCES,
|
input: KEY_PREFERENCES,
|
||||||
|
@ -139,6 +136,10 @@ export const keyCommands = [
|
||||||
})))
|
})))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const setKeyCommands = () => KeyCommands.setKeyCommands(keyCommands);
|
||||||
|
|
||||||
|
export const deleteKeyCommands = () => KeyCommands.deleteKeyCommands(keyCommands);
|
||||||
|
|
||||||
export const KEY_COMMAND = 'KEY_COMMAND';
|
export const KEY_COMMAND = 'KEY_COMMAND';
|
||||||
|
|
||||||
export const commandHandle = (event, key, flags = []) => {
|
export const commandHandle = (event, key, flags = []) => {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export const MAX_SIDEBAR_WIDTH = 321;
|
export const MAX_SIDEBAR_WIDTH = 321;
|
||||||
export const MAX_CONTENT_WIDTH = '90%';
|
|
||||||
export const MAX_SCREEN_CONTENT_WIDTH = '50%';
|
export const MAX_SCREEN_CONTENT_WIDTH = '50%';
|
||||||
export const MIN_WIDTH_SPLIT_LAYOUT = 700;
|
export const MIN_WIDTH_MASTER_DETAIL_LAYOUT = 700;
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
export const SET_CURRENT_SERVER = 'SET_CURRENT_SERVER';
|
|
||||||
export const SET_CUSTOM_EMOJIS = 'SET_CUSTOM_EMOJIS';
|
|
||||||
export const ADD_SETTINGS = 'ADD_SETTINGS';
|
|
||||||
export const CLEAR_SETTINGS = 'CLEAR_SETTINGS';
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -10,6 +9,7 @@ import KeyboardView from '../presentation/KeyboardView';
|
||||||
import StatusBar from './StatusBar';
|
import StatusBar from './StatusBar';
|
||||||
import AppVersion from './AppVersion';
|
import AppVersion from './AppVersion';
|
||||||
import { isTablet } from '../utils/deviceInfo';
|
import { isTablet } from '../utils/deviceInfo';
|
||||||
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scrollView: {
|
scrollView: {
|
||||||
|
@ -31,7 +31,7 @@ const FormContainer = ({ children, theme, testID }) => (
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}>
|
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}>
|
||||||
<SafeAreaView style={sharedStyles.container} forceInset={{ top: 'never' }} testID={testID}>
|
<SafeAreaView testID={testID} theme={theme} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||||
{children}
|
{children}
|
||||||
<AppVersion theme={theme} />
|
<AppVersion theme={theme} />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||||
|
import { View, StyleSheet } from 'react-native';
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
import { themedHeader } from '../../utils/navigation';
|
||||||
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
|
|
||||||
|
// Get from https://github.com/react-navigation/react-navigation/blob/master/packages/stack/src/views/Header/HeaderSegment.tsx#L69
|
||||||
|
export const headerHeight = isIOS ? 44 : 56;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
height: headerHeight,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
elevation: 4
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const Header = ({
|
||||||
|
theme, headerLeft, headerTitle, headerRight
|
||||||
|
}) => (
|
||||||
|
<SafeAreaView style={{ backgroundColor: themes[theme].headerBackground }} edges={['top', 'left', 'right']}>
|
||||||
|
<View style={[styles.container, { ...themedHeader(theme).headerStyle }]}>
|
||||||
|
{headerLeft ? headerLeft() : null}
|
||||||
|
{headerTitle ? headerTitle() : null}
|
||||||
|
{headerRight ? headerRight() : null}
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
|
||||||
|
Header.propTypes = {
|
||||||
|
theme: PropTypes.string,
|
||||||
|
headerLeft: PropTypes.element,
|
||||||
|
headerTitle: PropTypes.element,
|
||||||
|
headerRight: PropTypes.element
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
|
@ -5,7 +5,6 @@ import {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import { withNavigation } from 'react-navigation';
|
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
@ -361,4 +360,4 @@ const mapDispatchToProps = dispatch => ({
|
||||||
loginRequest: params => dispatch(loginRequestAction(params))
|
loginRequest: params => dispatch(loginRequestAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(withNavigation(LoginServices)));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginServices));
|
||||||
|
|
|
@ -32,7 +32,8 @@ class MessageActions extends React.Component {
|
||||||
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
|
Message_AllowEditing_BlockEditInMinutes: PropTypes.number,
|
||||||
Message_AllowPinning: PropTypes.bool,
|
Message_AllowPinning: PropTypes.bool,
|
||||||
Message_AllowStarring: PropTypes.bool,
|
Message_AllowStarring: PropTypes.bool,
|
||||||
Message_Read_Receipt_Store_Users: PropTypes.bool
|
Message_Read_Receipt_Store_Users: PropTypes.bool,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -254,7 +255,7 @@ class MessageActions extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnread = async() => {
|
handleUnread = async() => {
|
||||||
const { message, room } = this.props;
|
const { message, room, isMasterDetail } = this.props;
|
||||||
const { id: messageId, ts } = message;
|
const { id: messageId, ts } = message;
|
||||||
const { rid } = room;
|
const { rid } = room;
|
||||||
try {
|
try {
|
||||||
|
@ -270,8 +271,12 @@ class MessageActions extends React.Component {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.replace('RoomView');
|
||||||
|
} else {
|
||||||
Navigation.navigate('RoomsListView');
|
Navigation.navigate('RoomsListView');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
|
@ -376,8 +381,13 @@ class MessageActions extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateDiscussion = () => {
|
handleCreateDiscussion = () => {
|
||||||
const { message, room: channel } = this.props;
|
const { message, room: channel, isMasterDetail } = this.props;
|
||||||
Navigation.navigate('CreateDiscussionView', { message, channel });
|
const params = { message, channel, showCloseModal: true };
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params });
|
||||||
|
} else {
|
||||||
|
Navigation.navigate('NewMessageStackNavigator', { screen: 'CreateDiscussionView', params });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActionPress = (actionIndex) => {
|
handleActionPress = (actionIndex) => {
|
||||||
|
@ -450,7 +460,8 @@ const mapStateToProps = state => ({
|
||||||
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
||||||
Message_AllowPinning: state.settings.Message_AllowPinning,
|
Message_AllowPinning: state.settings.Message_AllowPinning,
|
||||||
Message_AllowStarring: state.settings.Message_AllowStarring,
|
Message_AllowStarring: state.settings.Message_AllowStarring,
|
||||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users
|
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(MessageActions);
|
export default connect(mapStateToProps)(MessageActions);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
View, SafeAreaView, PermissionsAndroid, Text
|
View, PermissionsAndroid, Text
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { AudioRecorder, AudioUtils } from 'react-native-audio';
|
import { AudioRecorder, AudioUtils } from 'react-native-audio';
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
|
@ -13,6 +13,7 @@ import I18n from '../../i18n';
|
||||||
import { isIOS, isAndroid } from '../../utils/deviceInfo';
|
import { isIOS, isAndroid } from '../../utils/deviceInfo';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import SafeAreaView from '../SafeAreaView';
|
||||||
|
|
||||||
export const _formatTime = function(seconds) {
|
export const _formatTime = function(seconds) {
|
||||||
let minutes = Math.floor(seconds / 60);
|
let minutes = Math.floor(seconds / 60);
|
||||||
|
@ -134,6 +135,7 @@ export default class extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
testID='messagebox-recording'
|
testID='messagebox-recording'
|
||||||
|
theme={theme}
|
||||||
style={[
|
style={[
|
||||||
styles.textBox,
|
styles.textBox,
|
||||||
{ borderTopColor: themes[theme].borderColor }
|
{ borderTopColor: themes[theme].borderColor }
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
modal: {
|
modal: {
|
||||||
|
@ -91,7 +90,7 @@ class UploadModal extends Component {
|
||||||
submit: PropTypes.func,
|
submit: PropTypes.func,
|
||||||
window: PropTypes.object,
|
window: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool
|
isMasterDetail: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -113,16 +112,11 @@ class UploadModal extends Component {
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const { name, description, file } = this.state;
|
const { name, description, file } = this.state;
|
||||||
const {
|
const { window, isVisible, theme } = this.props;
|
||||||
window, isVisible, split, theme
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (nextState.name !== name) {
|
if (nextState.name !== name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (nextProps.split !== split) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -194,9 +188,9 @@ class UploadModal extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPreview() {
|
renderPreview() {
|
||||||
const { file, split, theme } = this.props;
|
const { file, theme, isMasterDetail } = this.props;
|
||||||
if (file.mime && file.mime.match(/image/)) {
|
if (file.mime && file.mime.match(/image/)) {
|
||||||
return (<Image source={{ isStatic: true, uri: file.path }} style={[styles.image, split && styles.bigPreview]} />);
|
return (<Image source={{ isStatic: true, uri: file.path }} style={[styles.image, isMasterDetail && styles.bigPreview]} />);
|
||||||
}
|
}
|
||||||
if (file.mime && file.mime.match(/video/)) {
|
if (file.mime && file.mime.match(/video/)) {
|
||||||
return (
|
return (
|
||||||
|
@ -210,7 +204,7 @@ class UploadModal extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
window: { width }, isVisible, close, split, theme
|
window: { width }, isVisible, close, isMasterDetail, theme
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { name, description } = this.state;
|
const { name, description } = this.state;
|
||||||
return (
|
return (
|
||||||
|
@ -225,7 +219,7 @@ class UploadModal extends Component {
|
||||||
hideModalContentWhileAnimating
|
hideModalContentWhileAnimating
|
||||||
avoidKeyboard
|
avoidKeyboard
|
||||||
>
|
>
|
||||||
<View style={[styles.container, { width: width - 32, backgroundColor: themes[theme].chatComponentBackground }, split && [sharedStyles.modal, sharedStyles.modalFormSheet]]}>
|
<View style={[styles.container, { width: width - 32, backgroundColor: themes[theme].chatComponentBackground }, isMasterDetail && sharedStyles.modalFormSheet]}>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Upload_file_question_mark')}</Text>
|
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Upload_file_question_mark')}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -252,4 +246,4 @@ class UploadModal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default responsive(withTheme(withSplit(UploadModal)));
|
export default responsive(withTheme(UploadModal));
|
||||||
|
|
|
@ -95,6 +95,7 @@ class MessageBox extends Component {
|
||||||
typing: PropTypes.func,
|
typing: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
replyCancel: PropTypes.func,
|
replyCancel: PropTypes.func,
|
||||||
|
isMasterDetail: PropTypes.bool,
|
||||||
navigation: PropTypes.object
|
navigation: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,11 +184,14 @@ class MessageBox extends Component {
|
||||||
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
|
EventEmiter.addEventListener(KEY_COMMAND, this.handleCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.didFocusListener = navigation.addListener('didFocus', () => {
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
if (this.tracking && this.tracking.resetTracking) {
|
if (this.tracking && this.tracking.resetTracking) {
|
||||||
this.tracking.resetTracking();
|
this.tracking.resetTracking();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.unsubscribeBlur = navigation.addListener('blur', () => {
|
||||||
|
this.component?.blur();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
|
@ -268,8 +272,11 @@ class MessageBox extends Component {
|
||||||
if (this.getSlashCommands && this.getSlashCommands.stop) {
|
if (this.getSlashCommands && this.getSlashCommands.stop) {
|
||||||
this.getSlashCommands.stop();
|
this.getSlashCommands.stop();
|
||||||
}
|
}
|
||||||
if (this.didFocusListener && this.didFocusListener.remove) {
|
if (this.unsubscribeFocus) {
|
||||||
this.didFocusListener.remove();
|
this.unsubscribeFocus();
|
||||||
|
}
|
||||||
|
if (this.unsubscribeBlur) {
|
||||||
|
this.unsubscribeBlur();
|
||||||
}
|
}
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
EventEmiter.removeListener(KEY_COMMAND, this.handleCommands);
|
EventEmiter.removeListener(KEY_COMMAND, this.handleCommands);
|
||||||
|
@ -592,7 +599,13 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
createDiscussion = () => {
|
createDiscussion = () => {
|
||||||
Navigation.navigate('CreateDiscussionView', { channel: this.room });
|
const { isMasterDetail } = this.props;
|
||||||
|
const params = { channel: this.room, showCloseModal: true };
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params });
|
||||||
|
} else {
|
||||||
|
Navigation.navigate('NewMessageStackNavigator', { screen: 'CreateDiscussionView', params });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showUploadModal = (file) => {
|
showUploadModal = (file) => {
|
||||||
|
@ -875,7 +888,9 @@ class MessageBox extends Component {
|
||||||
render() {
|
render() {
|
||||||
console.count(`${ this.constructor.name }.render calls`);
|
console.count(`${ this.constructor.name }.render calls`);
|
||||||
const { showEmojiKeyboard, file } = this.state;
|
const { showEmojiKeyboard, file } = this.state;
|
||||||
const { user, baseUrl, theme } = this.props;
|
const {
|
||||||
|
user, baseUrl, theme, isMasterDetail
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<MessageboxContext.Provider
|
<MessageboxContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -903,6 +918,7 @@ class MessageBox extends Component {
|
||||||
file={file}
|
file={file}
|
||||||
close={() => this.setState({ file: {} })}
|
close={() => this.setState({ file: {} })}
|
||||||
submit={this.sendMediaMessage}
|
submit={this.sendMediaMessage}
|
||||||
|
isMasterDetail={isMasterDetail}
|
||||||
/>
|
/>
|
||||||
</MessageboxContext.Provider>
|
</MessageboxContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -910,6 +926,7 @@ class MessageBox extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
threadsEnabled: state.settings.Threads_enabled,
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
View, Text, FlatList, StyleSheet, SafeAreaView
|
View, Text, FlatList, StyleSheet
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
|
@ -12,8 +12,12 @@ import { CustomIcon } from '../lib/Icons';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
safeArea: {
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingVertical: 10
|
paddingVertical: 10
|
||||||
|
@ -95,7 +99,7 @@ const ModalContent = React.memo(({
|
||||||
}) => {
|
}) => {
|
||||||
if (message && message.reactions) {
|
if (message && message.reactions) {
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ flex: 1 }}>
|
<SafeAreaView theme={props.theme} style={styles.safeArea}>
|
||||||
<Touchable onPress={onClose}>
|
<Touchable onPress={onClose}>
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { SafeAreaView as SafeAreaContext } from 'react-native-safe-area-context';
|
||||||
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
view: {
|
||||||
|
flex: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SafeAreaView = React.memo(({
|
||||||
|
style, children, testID, theme, vertical = true, ...props
|
||||||
|
}) => (
|
||||||
|
<SafeAreaContext
|
||||||
|
style={[styles.view, { backgroundColor: themes[theme].auxiliaryBackground }, style]}
|
||||||
|
edges={vertical ? ['right', 'left'] : undefined}
|
||||||
|
testID={testID}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SafeAreaContext>
|
||||||
|
));
|
||||||
|
|
||||||
|
SafeAreaView.propTypes = {
|
||||||
|
testID: PropTypes.string,
|
||||||
|
theme: PropTypes.string,
|
||||||
|
vertical: PropTypes.bool,
|
||||||
|
style: PropTypes.object,
|
||||||
|
children: PropTypes.element
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SafeAreaView;
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
|
||||||
|
|
||||||
const StatusBar = React.memo(({ theme }) => {
|
const StatusBar = React.memo(({ theme }) => {
|
||||||
let barStyle = 'light-content';
|
let barStyle = 'light-content';
|
||||||
|
@ -18,4 +17,4 @@ StatusBar.propTypes = {
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTheme(StatusBar);
|
export default StatusBar;
|
||||||
|
|
|
@ -5,12 +5,12 @@ import PropTypes from 'prop-types';
|
||||||
import { sha256 } from 'js-sha256';
|
import { sha256 } from 'js-sha256';
|
||||||
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 { connect } from 'react-redux';
|
||||||
|
|
||||||
import TextInput from '../TextInput';
|
import TextInput from '../TextInput';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
@ -36,7 +36,7 @@ const methods = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const TwoFactor = React.memo(({ theme, split }) => {
|
const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [data, setData] = useState({});
|
const [data, setData] = useState({});
|
||||||
const [code, setCode] = useState('');
|
const [code, setCode] = useState('');
|
||||||
|
@ -93,7 +93,7 @@ const TwoFactor = React.memo(({ theme, split }) => {
|
||||||
hideModalContentWhileAnimating
|
hideModalContentWhileAnimating
|
||||||
>
|
>
|
||||||
<View style={styles.container} testID='two-factor'>
|
<View style={styles.container} testID='two-factor'>
|
||||||
<View style={[styles.content, split && [sharedStyles.modal, sharedStyles.modalFormSheet], { backgroundColor: themes[theme].backgroundColor }]}>
|
<View style={[styles.content, isMasterDetail && [sharedStyles.modalFormSheet, styles.tablet], { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
<Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text>
|
<Text style={[styles.title, { color }]}>{I18n.t(method?.title || 'Two_Factor_Authentication')}</Text>
|
||||||
{method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null}
|
{method?.text ? <Text style={[styles.subtitle, { color }]}>{I18n.t(method.text)}</Text> : null}
|
||||||
<TextInput
|
<TextInput
|
||||||
|
@ -134,7 +134,11 @@ const TwoFactor = React.memo(({ theme, split }) => {
|
||||||
});
|
});
|
||||||
TwoFactor.propTypes = {
|
TwoFactor.propTypes = {
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool
|
isMasterDetail: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withSplit(withTheme(TwoFactor));
|
const mapStateToProps = state => ({
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(withTheme(TwoFactor));
|
||||||
|
|
|
@ -37,5 +37,8 @@ export default StyleSheet.create({
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-between'
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
tablet: {
|
||||||
|
height: undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { CustomIcon } from '../../lib/Icons';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
|
@ -98,7 +97,6 @@ class MessageAudio extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
file: PropTypes.object.isRequired,
|
file: PropTypes.object.isRequired,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool,
|
|
||||||
getCustomEmoji: PropTypes.func
|
getCustomEmoji: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +136,7 @@ class MessageAudio extends React.Component {
|
||||||
const {
|
const {
|
||||||
currentTime, duration, paused, loading
|
currentTime, duration, paused, loading
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { file, split, theme } = this.props;
|
const { file, theme } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -154,9 +152,6 @@ class MessageAudio extends React.Component {
|
||||||
if (!equal(nextProps.file, file)) {
|
if (!equal(nextProps.file, file)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (nextProps.split !== split) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (nextState.loading !== loading) {
|
if (nextState.loading !== loading) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -248,9 +243,7 @@ class MessageAudio extends React.Component {
|
||||||
const {
|
const {
|
||||||
loading, paused, currentTime, duration
|
loading, paused, currentTime, duration
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const { file, getCustomEmoji, theme } = this.props;
|
||||||
file, getCustomEmoji, split, theme
|
|
||||||
} = this.props;
|
|
||||||
const { description } = file;
|
const { description } = file;
|
||||||
const { baseUrl, user } = this.context;
|
const { baseUrl, user } = this.context;
|
||||||
|
|
||||||
|
@ -263,8 +256,7 @@ class MessageAudio extends React.Component {
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.audioContainer,
|
styles.audioContainer,
|
||||||
{ backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor },
|
{ backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor }
|
||||||
split && sharedStyles.tabletContent
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Button loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} />
|
<Button loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} />
|
||||||
|
@ -289,4 +281,4 @@ class MessageAudio extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withSplit(MessageAudio);
|
export default MessageAudio;
|
||||||
|
|
|
@ -10,19 +10,17 @@ import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
|
||||||
const Button = React.memo(({
|
const Button = React.memo(({
|
||||||
children, onPress, split, theme
|
children, onPress, theme
|
||||||
}) => (
|
}) => (
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[styles.imageContainer, split && sharedStyles.tabletContent]}
|
style={styles.imageContainer}
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -42,7 +40,7 @@ export const MessageImage = React.memo(({ img, theme }) => (
|
||||||
));
|
));
|
||||||
|
|
||||||
const ImageContainer = React.memo(({
|
const ImageContainer = React.memo(({
|
||||||
file, imageUrl, showAttachment, getCustomEmoji, split, theme
|
file, imageUrl, showAttachment, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
||||||
|
@ -54,7 +52,7 @@ const ImageContainer = React.memo(({
|
||||||
|
|
||||||
if (file.description) {
|
if (file.description) {
|
||||||
return (
|
return (
|
||||||
<Button split={split} theme={theme} onPress={onPress}>
|
<Button theme={theme} onPress={onPress}>
|
||||||
<View>
|
<View>
|
||||||
<MessageImage img={img} theme={theme} />
|
<MessageImage img={img} theme={theme} />
|
||||||
<Markdown msg={file.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Markdown msg={file.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
|
@ -64,19 +62,18 @@ const ImageContainer = React.memo(({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button split={split} theme={theme} onPress={onPress}>
|
<Button theme={theme} onPress={onPress}>
|
||||||
<MessageImage img={img} theme={theme} />
|
<MessageImage img={img} theme={theme} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => equal(prevProps.file, nextProps.file) && prevProps.split === nextProps.split && prevProps.theme === nextProps.theme);
|
}, (prevProps, nextProps) => equal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme);
|
||||||
|
|
||||||
ImageContainer.propTypes = {
|
ImageContainer.propTypes = {
|
||||||
file: PropTypes.object,
|
file: PropTypes.object,
|
||||||
imageUrl: PropTypes.string,
|
imageUrl: PropTypes.string,
|
||||||
showAttachment: PropTypes.func,
|
showAttachment: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func
|
||||||
split: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
ImageContainer.displayName = 'MessageImageContainer';
|
ImageContainer.displayName = 'MessageImageContainer';
|
||||||
|
|
||||||
|
@ -89,9 +86,8 @@ ImageContainer.displayName = 'MessageImage';
|
||||||
Button.propTypes = {
|
Button.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
split: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
ImageContainer.displayName = 'MessageButton';
|
ImageContainer.displayName = 'MessageButton';
|
||||||
|
|
||||||
export default withSplit(ImageContainer);
|
export default ImageContainer;
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Markdown from '../markdown';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -126,7 +125,7 @@ const Fields = React.memo(({ attachment, theme }) => {
|
||||||
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
||||||
|
|
||||||
const Reply = React.memo(({
|
const Reply = React.memo(({
|
||||||
attachment, timeFormat, index, getCustomEmoji, split, theme
|
attachment, timeFormat, index, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!attachment) {
|
if (!attachment) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -156,8 +155,7 @@ const Reply = React.memo(({
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].chatComponentBackground,
|
backgroundColor: themes[theme].chatComponentBackground,
|
||||||
borderColor: themes[theme].borderColor
|
borderColor: themes[theme].borderColor
|
||||||
},
|
}
|
||||||
split && sharedStyles.tabletContent
|
|
||||||
]}
|
]}
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
>
|
>
|
||||||
|
@ -173,15 +171,14 @@ const Reply = React.memo(({
|
||||||
</View>
|
</View>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => isEqual(prevProps.attachment, nextProps.attachment) && prevProps.split === nextProps.split && prevProps.theme === nextProps.theme);
|
}, (prevProps, nextProps) => isEqual(prevProps.attachment, nextProps.attachment) && prevProps.theme === nextProps.theme);
|
||||||
|
|
||||||
Reply.propTypes = {
|
Reply.propTypes = {
|
||||||
attachment: PropTypes.object,
|
attachment: PropTypes.object,
|
||||||
timeFormat: PropTypes.string,
|
timeFormat: PropTypes.string,
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func
|
||||||
split: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
Reply.displayName = 'MessageReply';
|
Reply.displayName = 'MessageReply';
|
||||||
|
|
||||||
|
@ -205,4 +202,4 @@ Fields.propTypes = {
|
||||||
};
|
};
|
||||||
Fields.displayName = 'MessageReplyFields';
|
Fields.displayName = 'MessageReplyFields';
|
||||||
|
|
||||||
export default withSplit(Reply);
|
export default Reply;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import openLink from '../../utils/openLink';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import { LISTENER } from '../Toast';
|
import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -80,9 +79,7 @@ const UrlContent = React.memo(({ title, description, theme }) => (
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const Url = React.memo(({
|
const Url = React.memo(({ url, index, theme }) => {
|
||||||
url, index, split, theme
|
|
||||||
}) => {
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -105,8 +102,7 @@ const Url = React.memo(({
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].chatComponentBackground,
|
backgroundColor: themes[theme].chatComponentBackground,
|
||||||
borderColor: themes[theme].borderColor
|
borderColor: themes[theme].borderColor
|
||||||
},
|
}
|
||||||
split && sharedStyles.tabletContent
|
|
||||||
]}
|
]}
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
>
|
>
|
||||||
|
@ -116,19 +112,17 @@ const Url = React.memo(({
|
||||||
</>
|
</>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.theme === newProps.theme);
|
||||||
|
|
||||||
const Urls = React.memo(({
|
const Urls = React.memo(({ urls, theme }) => {
|
||||||
urls, split, theme
|
|
||||||
}) => {
|
|
||||||
if (!urls || urls.length === 0) {
|
if (!urls || urls.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return urls.map((url, index) => (
|
return urls.map((url, index) => (
|
||||||
<Url url={url} key={url.url} index={index} split={split} theme={theme} />
|
<Url url={url} key={url.url} index={index} theme={theme} />
|
||||||
));
|
));
|
||||||
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme);
|
||||||
|
|
||||||
UrlImage.propTypes = {
|
UrlImage.propTypes = {
|
||||||
image: PropTypes.string
|
image: PropTypes.string
|
||||||
|
@ -145,16 +139,14 @@ UrlContent.displayName = 'MessageUrlContent';
|
||||||
Url.propTypes = {
|
Url.propTypes = {
|
||||||
url: PropTypes.object.isRequired,
|
url: PropTypes.object.isRequired,
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
split: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
Url.displayName = 'MessageUrl';
|
Url.displayName = 'MessageUrl';
|
||||||
|
|
||||||
Urls.propTypes = {
|
Urls.propTypes = {
|
||||||
urls: PropTypes.array,
|
urls: PropTypes.array,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
split: PropTypes.bool
|
|
||||||
};
|
};
|
||||||
Urls.displayName = 'MessageUrls';
|
Urls.displayName = 'MessageUrls';
|
||||||
|
|
||||||
export default withTheme(withSplit(Urls));
|
export default withTheme(Urls);
|
||||||
|
|
|
@ -6,11 +6,10 @@ import isEqual from 'deep-equal';
|
||||||
import Touchable from './Touchable';
|
import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
||||||
|
@ -46,7 +45,7 @@ const Video = React.memo(({
|
||||||
<>
|
<>
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[styles.button, { backgroundColor: themes[theme].videoBackground }, isTablet && sharedStyles.tabletContent]}
|
style={[styles.button, { backgroundColor: themes[theme].videoBackground }]}
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
>
|
>
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
|
|
649
app/index.js
649
app/index.js
|
@ -1,16 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { Linking, Dimensions } from 'react-native';
|
||||||
View, Linking, BackHandler, ScrollView
|
|
||||||
} from 'react-native';
|
|
||||||
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
|
|
||||||
import { createStackNavigator } from 'react-navigation-stack';
|
|
||||||
import { createDrawerNavigator } from 'react-navigation-drawer';
|
|
||||||
import { AppearanceProvider } from 'react-native-appearance';
|
import { AppearanceProvider } from 'react-native-appearance';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import RNUserDefaults from 'rn-user-defaults';
|
import RNUserDefaults from 'rn-user-defaults';
|
||||||
import Modal from 'react-native-modal';
|
import { KeyCommandsEmitter } from 'react-native-keycommands';
|
||||||
import KeyCommands, { KeyCommandsEmitter } from 'react-native-keycommands';
|
import RNScreens from 'react-native-screens';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
defaultTheme,
|
defaultTheme,
|
||||||
|
@ -19,40 +13,25 @@ import {
|
||||||
unsubscribeTheme
|
unsubscribeTheme
|
||||||
} from './utils/theme';
|
} from './utils/theme';
|
||||||
import EventEmitter from './utils/events';
|
import EventEmitter from './utils/events';
|
||||||
import { appInit, appInitLocalSettings } from './actions';
|
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
||||||
import { deepLinkingOpen } from './actions/deepLinking';
|
import { deepLinkingOpen } from './actions/deepLinking';
|
||||||
import Navigation from './lib/Navigation';
|
|
||||||
import Sidebar from './views/SidebarView';
|
|
||||||
import parseQuery from './lib/methods/helpers/parseQuery';
|
import parseQuery from './lib/methods/helpers/parseQuery';
|
||||||
import { initializePushNotifications, onNotification } from './notifications/push';
|
import { initializePushNotifications, onNotification } from './notifications/push';
|
||||||
import store from './lib/createStore';
|
import store from './lib/createStore';
|
||||||
import NotificationBadge from './notifications/inApp';
|
|
||||||
import {
|
|
||||||
defaultHeader, onNavigationStateChange, cardStyle, getActiveRouteName
|
|
||||||
} from './utils/navigation';
|
|
||||||
import { loggerConfig, analytics } from './utils/log';
|
import { loggerConfig, analytics } from './utils/log';
|
||||||
import Toast from './containers/Toast';
|
|
||||||
import { ThemeContext } from './theme';
|
import { ThemeContext } from './theme';
|
||||||
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
||||||
import { MIN_WIDTH_SPLIT_LAYOUT } from './constants/tablet';
|
import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './constants/tablet';
|
||||||
import {
|
import {
|
||||||
isTablet, isSplited, isIOS, setWidth, supportSystemTheme, isAndroid
|
isTablet, supportSystemTheme
|
||||||
} from './utils/deviceInfo';
|
} from './utils/deviceInfo';
|
||||||
import { KEY_COMMAND } from './commands';
|
import { KEY_COMMAND } from './commands';
|
||||||
import Tablet, { initTabletNav } from './tablet';
|
import AppContainer from './AppContainer';
|
||||||
import sharedStyles from './views/Styles';
|
|
||||||
import { SplitContext } from './split';
|
|
||||||
import TwoFactor from './containers/TwoFactor';
|
import TwoFactor from './containers/TwoFactor';
|
||||||
|
|
||||||
import RoomsListView from './views/RoomsListView';
|
|
||||||
import RoomView from './views/RoomView';
|
|
||||||
import ScreenLockedView from './views/ScreenLockedView';
|
import ScreenLockedView from './views/ScreenLockedView';
|
||||||
import ChangePasscodeView from './views/ChangePasscodeView';
|
import ChangePasscodeView from './views/ChangePasscodeView';
|
||||||
|
|
||||||
if (isIOS) {
|
RNScreens.enableScreens();
|
||||||
const RNScreens = require('react-native-screens');
|
|
||||||
RNScreens.useScreens();
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseDeepLinking = (url) => {
|
const parseDeepLinking = (url) => {
|
||||||
if (url) {
|
if (url) {
|
||||||
|
@ -68,543 +47,12 @@ const parseDeepLinking = (url) => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Outside
|
|
||||||
const OutsideStack = createStackNavigator({
|
|
||||||
OnboardingView: {
|
|
||||||
getScreen: () => require('./views/OnboardingView').default,
|
|
||||||
header: null
|
|
||||||
},
|
|
||||||
NewServerView: {
|
|
||||||
getScreen: () => require('./views/NewServerView').default
|
|
||||||
},
|
|
||||||
WorkspaceView: {
|
|
||||||
getScreen: () => require('./views/WorkspaceView').default
|
|
||||||
},
|
|
||||||
LoginView: {
|
|
||||||
getScreen: () => require('./views/LoginView').default
|
|
||||||
},
|
|
||||||
ForgotPasswordView: {
|
|
||||||
getScreen: () => require('./views/ForgotPasswordView').default
|
|
||||||
},
|
|
||||||
RegisterView: {
|
|
||||||
getScreen: () => require('./views/RegisterView').default
|
|
||||||
},
|
|
||||||
LegalView: {
|
|
||||||
getScreen: () => require('./views/LegalView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const AuthenticationWebViewStack = createStackNavigator({
|
|
||||||
AuthenticationWebView: {
|
|
||||||
getScreen: () => require('./views/AuthenticationWebView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const OutsideStackModal = createStackNavigator({
|
|
||||||
OutsideStack,
|
|
||||||
AuthenticationWebViewStack
|
|
||||||
},
|
|
||||||
{
|
|
||||||
mode: 'modal',
|
|
||||||
headerMode: 'none',
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const RoomRoutes = {
|
|
||||||
RoomView,
|
|
||||||
ThreadMessagesView: {
|
|
||||||
getScreen: () => require('./views/ThreadMessagesView').default
|
|
||||||
},
|
|
||||||
MarkdownTableView: {
|
|
||||||
getScreen: () => require('./views/MarkdownTableView').default
|
|
||||||
},
|
|
||||||
ReadReceiptsView: {
|
|
||||||
getScreen: () => require('./views/ReadReceiptView').default
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inside
|
|
||||||
const ChatsStack = createStackNavigator({
|
|
||||||
RoomsListView,
|
|
||||||
RoomActionsView: {
|
|
||||||
getScreen: () => require('./views/RoomActionsView').default
|
|
||||||
},
|
|
||||||
RoomInfoView: {
|
|
||||||
getScreen: () => require('./views/RoomInfoView').default
|
|
||||||
},
|
|
||||||
RoomInfoEditView: {
|
|
||||||
getScreen: () => require('./views/RoomInfoEditView').default
|
|
||||||
},
|
|
||||||
RoomMembersView: {
|
|
||||||
getScreen: () => require('./views/RoomMembersView').default
|
|
||||||
},
|
|
||||||
SearchMessagesView: {
|
|
||||||
getScreen: () => require('./views/SearchMessagesView').default
|
|
||||||
},
|
|
||||||
SelectedUsersView: {
|
|
||||||
getScreen: () => require('./views/SelectedUsersView').default
|
|
||||||
},
|
|
||||||
InviteUsersView: {
|
|
||||||
getScreen: () => require('./views/InviteUsersView').default
|
|
||||||
},
|
|
||||||
InviteUsersEditView: {
|
|
||||||
getScreen: () => require('./views/InviteUsersEditView').default
|
|
||||||
},
|
|
||||||
MessagesView: {
|
|
||||||
getScreen: () => require('./views/MessagesView').default
|
|
||||||
},
|
|
||||||
AutoTranslateView: {
|
|
||||||
getScreen: () => require('./views/AutoTranslateView').default
|
|
||||||
},
|
|
||||||
DirectoryView: {
|
|
||||||
getScreen: () => require('./views/DirectoryView').default
|
|
||||||
},
|
|
||||||
NotificationPrefView: {
|
|
||||||
getScreen: () => require('./views/NotificationPreferencesView').default
|
|
||||||
},
|
|
||||||
VisitorNavigationView: {
|
|
||||||
getScreen: () => require('./views/VisitorNavigationView').default
|
|
||||||
},
|
|
||||||
ForwardLivechatView: {
|
|
||||||
getScreen: () => require('./views/ForwardLivechatView').default
|
|
||||||
},
|
|
||||||
LivechatEditView: {
|
|
||||||
getScreen: () => require('./views/LivechatEditView').default
|
|
||||||
},
|
|
||||||
PickerView: {
|
|
||||||
getScreen: () => require('./views/PickerView').default
|
|
||||||
},
|
|
||||||
...RoomRoutes
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
// Inside
|
|
||||||
const RoomStack = createStackNavigator({
|
|
||||||
...RoomRoutes
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
ChatsStack.navigationOptions = ({ navigation }) => {
|
|
||||||
let drawerLockMode = 'unlocked';
|
|
||||||
if (navigation.state.index > 0 || isSplited()) {
|
|
||||||
drawerLockMode = 'locked-closed';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
drawerLockMode
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProfileStack = createStackNavigator({
|
|
||||||
ProfileView: {
|
|
||||||
getScreen: () => require('./views/ProfileView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
ProfileStack.navigationOptions = ({ navigation }) => {
|
|
||||||
let drawerLockMode = 'unlocked';
|
|
||||||
if (navigation.state.index > 0) {
|
|
||||||
drawerLockMode = 'locked-closed';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
drawerLockMode
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const SettingsStack = createStackNavigator({
|
|
||||||
SettingsView: {
|
|
||||||
getScreen: () => require('./views/SettingsView').default
|
|
||||||
},
|
|
||||||
LanguageView: {
|
|
||||||
getScreen: () => require('./views/LanguageView').default
|
|
||||||
},
|
|
||||||
ThemeView: {
|
|
||||||
getScreen: () => require('./views/ThemeView').default
|
|
||||||
},
|
|
||||||
DefaultBrowserView: {
|
|
||||||
getScreen: () => require('./views/DefaultBrowserView').default
|
|
||||||
},
|
|
||||||
ScreenLockConfigView: {
|
|
||||||
getScreen: () => require('./views/ScreenLockConfigView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const AdminPanelStack = createStackNavigator({
|
|
||||||
AdminPanelView: {
|
|
||||||
getScreen: () => require('./views/AdminPanelView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
SettingsStack.navigationOptions = ({ navigation }) => {
|
|
||||||
let drawerLockMode = 'unlocked';
|
|
||||||
if (navigation.state.index > 0) {
|
|
||||||
drawerLockMode = 'locked-closed';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
drawerLockMode
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const ChatsDrawer = createDrawerNavigator({
|
|
||||||
ChatsStack,
|
|
||||||
ProfileStack,
|
|
||||||
SettingsStack,
|
|
||||||
AdminPanelStack
|
|
||||||
}, {
|
|
||||||
contentComponent: Sidebar,
|
|
||||||
overlayColor: '#00000090'
|
|
||||||
});
|
|
||||||
|
|
||||||
const NewMessageStack = createStackNavigator({
|
|
||||||
NewMessageView: {
|
|
||||||
getScreen: () => require('./views/NewMessageView').default
|
|
||||||
},
|
|
||||||
SelectedUsersViewCreateChannel: {
|
|
||||||
getScreen: () => require('./views/SelectedUsersView').default
|
|
||||||
},
|
|
||||||
CreateChannelView: {
|
|
||||||
getScreen: () => require('./views/CreateChannelView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const AttachmentStack = createStackNavigator({
|
|
||||||
AttachmentView: {
|
|
||||||
getScreen: () => require('./views/AttachmentView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const ModalBlockStack = createStackNavigator({
|
|
||||||
ModalBlockView: {
|
|
||||||
getScreen: () => require('./views/ModalBlockView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
mode: 'modal',
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const CreateDiscussionStack = createStackNavigator({
|
|
||||||
CreateDiscussionView: {
|
|
||||||
getScreen: () => require('./views/CreateDiscussionView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const StatusStack = createStackNavigator({
|
|
||||||
StatusView: {
|
|
||||||
getScreen: () => require('./views/StatusView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const InsideStackModal = createStackNavigator({
|
|
||||||
Main: ChatsDrawer,
|
|
||||||
NewMessageStack,
|
|
||||||
AttachmentStack,
|
|
||||||
ModalBlockStack,
|
|
||||||
StatusStack,
|
|
||||||
CreateDiscussionStack,
|
|
||||||
JitsiMeetView: {
|
|
||||||
getScreen: () => require('./views/JitsiMeetView').default
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
mode: 'modal',
|
|
||||||
headerMode: 'none',
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const SetUsernameStack = createStackNavigator({
|
|
||||||
SetUsernameView: {
|
|
||||||
getScreen: () => require('./views/SetUsernameView').default
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
class CustomInsideStack extends React.Component {
|
|
||||||
static router = InsideStackModal.router;
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
screenProps: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { navigation, screenProps } = this.props;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<InsideStackModal navigation={navigation} screenProps={screenProps} />
|
|
||||||
{ !isTablet ? <NotificationBadge navigation={navigation} /> : null }
|
|
||||||
{ !isTablet ? <Toast /> : null }
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomRoomStack extends React.Component {
|
|
||||||
static router = RoomStack.router;
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
screenProps: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { navigation, screenProps } = this.props;
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<RoomStack navigation={navigation} screenProps={screenProps} />
|
|
||||||
<Toast />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MessagesStack = createStackNavigator({
|
|
||||||
NewMessageView: {
|
|
||||||
getScreen: () => require('./views/NewMessageView').default
|
|
||||||
},
|
|
||||||
SelectedUsersViewCreateChannel: {
|
|
||||||
getScreen: () => require('./views/SelectedUsersView').default
|
|
||||||
},
|
|
||||||
CreateChannelView: {
|
|
||||||
getScreen: () => require('./views/CreateChannelView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const DirectoryStack = createStackNavigator({
|
|
||||||
DirectoryView: {
|
|
||||||
getScreen: () => require('./views/DirectoryView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const SidebarStack = createStackNavigator({
|
|
||||||
SettingsView: {
|
|
||||||
getScreen: () => require('./views/SettingsView').default
|
|
||||||
},
|
|
||||||
ProfileView: {
|
|
||||||
getScreen: () => require('./views/ProfileView').default
|
|
||||||
},
|
|
||||||
AdminPanelView: {
|
|
||||||
getScreen: () => require('./views/AdminPanelView').default
|
|
||||||
},
|
|
||||||
StatusView: {
|
|
||||||
getScreen: () => require('./views/StatusView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const RoomActionsStack = createStackNavigator({
|
|
||||||
RoomActionsView: {
|
|
||||||
getScreen: () => require('./views/RoomActionsView').default
|
|
||||||
},
|
|
||||||
RoomInfoView: {
|
|
||||||
getScreen: () => require('./views/RoomInfoView').default
|
|
||||||
},
|
|
||||||
RoomInfoEditView: {
|
|
||||||
getScreen: () => require('./views/RoomInfoEditView').default
|
|
||||||
},
|
|
||||||
RoomMembersView: {
|
|
||||||
getScreen: () => require('./views/RoomMembersView').default
|
|
||||||
},
|
|
||||||
SearchMessagesView: {
|
|
||||||
getScreen: () => require('./views/SearchMessagesView').default
|
|
||||||
},
|
|
||||||
SelectedUsersView: {
|
|
||||||
getScreen: () => require('./views/SelectedUsersView').default
|
|
||||||
},
|
|
||||||
MessagesView: {
|
|
||||||
getScreen: () => require('./views/MessagesView').default
|
|
||||||
},
|
|
||||||
AutoTranslateView: {
|
|
||||||
getScreen: () => require('./views/AutoTranslateView').default
|
|
||||||
},
|
|
||||||
ReadReceiptsView: {
|
|
||||||
getScreen: () => require('./views/ReadReceiptView').default
|
|
||||||
},
|
|
||||||
NotificationPrefView: {
|
|
||||||
getScreen: () => require('./views/NotificationPreferencesView').default
|
|
||||||
},
|
|
||||||
AttachmentView: {
|
|
||||||
getScreen: () => require('./views/AttachmentView').default
|
|
||||||
},
|
|
||||||
PickerView: {
|
|
||||||
getScreen: () => require('./views/PickerView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const ModalSwitch = createSwitchNavigator({
|
|
||||||
MessagesStack,
|
|
||||||
DirectoryStack,
|
|
||||||
SidebarStack,
|
|
||||||
RoomActionsStack,
|
|
||||||
SettingsStack,
|
|
||||||
ModalBlockStack,
|
|
||||||
CreateDiscussionStack,
|
|
||||||
AuthLoading: () => null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
initialRouteName: 'AuthLoading'
|
|
||||||
});
|
|
||||||
|
|
||||||
class CustomModalStack extends React.Component {
|
|
||||||
static router = ModalSwitch.router;
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
showModal: PropTypes.bool,
|
|
||||||
closeModal: PropTypes.func,
|
|
||||||
screenProps: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.closeModal);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.backHandler.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
closeModal = () => {
|
|
||||||
const { closeModal, navigation } = this.props;
|
|
||||||
const { state } = navigation;
|
|
||||||
if (state && state.routes[state.index] && state.routes[state.index].index === 0) {
|
|
||||||
closeModal();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (state && state.routes[state.index] && state.routes[state.index].routes && state.routes[state.index].routes.length > 1) {
|
|
||||||
navigation.goBack();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
navigation, showModal, closeModal, screenProps
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const pageSheetViews = ['AttachmentView'];
|
|
||||||
const pageSheet = pageSheetViews.includes(getActiveRouteName(navigation.state));
|
|
||||||
|
|
||||||
const androidProps = isAndroid && !pageSheet && {
|
|
||||||
style: { marginBottom: 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
let content = (
|
|
||||||
<View style={[sharedStyles.modal, pageSheet ? sharedStyles.modalPageSheet : sharedStyles.modalFormSheet]}>
|
|
||||||
<ModalSwitch navigation={navigation} screenProps={{ ...screenProps, closeModal: this.closeModal }} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isAndroid && !pageSheet) {
|
|
||||||
content = (
|
|
||||||
<ScrollView overScrollMode='never'>
|
|
||||||
{content}
|
|
||||||
</ScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
useNativeDriver
|
|
||||||
coverScreen={false}
|
|
||||||
isVisible={showModal}
|
|
||||||
onBackdropPress={closeModal}
|
|
||||||
hideModalContentWhileAnimating
|
|
||||||
avoidKeyboard
|
|
||||||
{...androidProps}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomNotificationStack extends React.Component {
|
|
||||||
static router = InsideStackModal.router;
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
screenProps: PropTypes.object
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { navigation, screenProps } = this.props;
|
|
||||||
return <NotificationBadge navigation={navigation} screenProps={screenProps} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const App = createAppContainer(createSwitchNavigator(
|
|
||||||
{
|
|
||||||
OutsideStack: OutsideStackModal,
|
|
||||||
InsideStack: CustomInsideStack,
|
|
||||||
AuthLoading: {
|
|
||||||
getScreen: () => require('./views/AuthLoadingView').default
|
|
||||||
},
|
|
||||||
SetUsernameStack
|
|
||||||
},
|
|
||||||
{
|
|
||||||
initialRouteName: 'AuthLoading'
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
export const RoomContainer = createAppContainer(CustomRoomStack);
|
|
||||||
|
|
||||||
export const ModalContainer = createAppContainer(CustomModalStack);
|
|
||||||
|
|
||||||
export const NotificationContainer = createAppContainer(CustomNotificationStack);
|
|
||||||
|
|
||||||
export default class Root extends React.Component {
|
export default class Root extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.init();
|
this.init();
|
||||||
this.initCrashReport();
|
this.initCrashReport();
|
||||||
this.state = {
|
this.state = {
|
||||||
split: false,
|
|
||||||
inside: false,
|
|
||||||
showModal: false,
|
|
||||||
theme: defaultTheme(),
|
theme: defaultTheme(),
|
||||||
themePreferences: {
|
themePreferences: {
|
||||||
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
||||||
|
@ -625,22 +73,12 @@ export default class Root extends React.Component {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
Dimensions.addEventListener('change', this.onDimensionsChange);
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
componentDidUpdate(_, prevState) {
|
|
||||||
if (isTablet) {
|
|
||||||
const { split, inside } = this.state;
|
|
||||||
if (inside && split !== prevState.split) {
|
|
||||||
// Reset app on split mode changes
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
this.closeModal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.listenerTimeout);
|
clearTimeout(this.listenerTimeout);
|
||||||
|
Dimensions.removeEventListener('change', this.onDimensionsChange);
|
||||||
|
|
||||||
unsubscribeTheme();
|
unsubscribeTheme();
|
||||||
|
|
||||||
|
@ -663,6 +101,22 @@ export default class Root extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMasterDetail = (width) => {
|
||||||
|
if (!isTablet) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMasterDetail = (width) => {
|
||||||
|
const isMasterDetail = this.getMasterDetail(width);
|
||||||
|
store.dispatch(setMasterDetailAction(isMasterDetail));
|
||||||
|
};
|
||||||
|
|
||||||
|
onDimensionsChange = ({ window: { width } }) => {
|
||||||
|
this.setMasterDetail(width);
|
||||||
|
}
|
||||||
|
|
||||||
setTheme = (newTheme = {}) => {
|
setTheme = (newTheme = {}) => {
|
||||||
// change theme state
|
// change theme state
|
||||||
this.setState(prevState => newThemeState(prevState, newTheme), () => {
|
this.setState(prevState => newThemeState(prevState, newTheme), () => {
|
||||||
|
@ -672,12 +126,14 @@ export default class Root extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initTablet = async() => {
|
initTablet = () => {
|
||||||
initTabletNav(args => this.setState(args));
|
const { width } = Dimensions.get('window');
|
||||||
await KeyCommands.setKeyCommands([]);
|
this.setMasterDetail(width);
|
||||||
this.onKeyCommands = KeyCommandsEmitter.addListener(
|
this.onKeyCommands = KeyCommandsEmitter.addListener(
|
||||||
'onKeyCommand',
|
'onKeyCommand',
|
||||||
command => EventEmitter.emit(KEY_COMMAND, { event: command })
|
(command) => {
|
||||||
|
EventEmitter.emit(KEY_COMMAND, { event: command });
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,45 +148,8 @@ export default class Root extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayout = ({ nativeEvent: { layout: { width } } }) => (isTablet ? this.setSplit(width) : null);
|
|
||||||
|
|
||||||
setSplit = (width) => {
|
|
||||||
this.setState({ split: width > MIN_WIDTH_SPLIT_LAYOUT });
|
|
||||||
setWidth(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeModal = () => this.setState({ showModal: false });
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { split, themePreferences, theme } = this.state;
|
const { themePreferences, theme } = this.state;
|
||||||
|
|
||||||
let content = (
|
|
||||||
<App
|
|
||||||
ref={(navigatorRef) => {
|
|
||||||
Navigation.setTopLevelNavigator(navigatorRef);
|
|
||||||
}}
|
|
||||||
screenProps={{ split, theme }}
|
|
||||||
onNavigationStateChange={onNavigationStateChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isTablet) {
|
|
||||||
const { inside, showModal } = this.state;
|
|
||||||
content = (
|
|
||||||
<SplitContext.Provider value={{ split }}>
|
|
||||||
<Tablet
|
|
||||||
theme={theme}
|
|
||||||
tablet={split}
|
|
||||||
inside={inside}
|
|
||||||
showModal={showModal}
|
|
||||||
closeModal={this.closeModal}
|
|
||||||
onLayout={this.onLayout}
|
|
||||||
>
|
|
||||||
{content}
|
|
||||||
</Tablet>
|
|
||||||
</SplitContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<AppearanceProvider>
|
<AppearanceProvider>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
@ -741,7 +160,7 @@ export default class Root extends React.Component {
|
||||||
setTheme: this.setTheme
|
setTheme: this.setTheme
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
<AppContainer />
|
||||||
<TwoFactor />
|
<TwoFactor />
|
||||||
<ScreenLockedView />
|
<ScreenLockedView />
|
||||||
<ChangePasscodeView />
|
<ChangePasscodeView />
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { NavigationActions } from 'react-navigation';
|
|
||||||
|
|
||||||
let _navigatorModal;
|
|
||||||
|
|
||||||
function setTopLevelNavigator(navigatorRef) {
|
|
||||||
_navigatorModal = navigatorRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate(routeName, params) {
|
|
||||||
_navigatorModal.dispatch(
|
|
||||||
NavigationActions.navigate({
|
|
||||||
routeName,
|
|
||||||
params
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
navigate,
|
|
||||||
setTopLevelNavigator
|
|
||||||
};
|
|
|
@ -1,28 +1,25 @@
|
||||||
import { NavigationActions } from 'react-navigation';
|
import * as React from 'react';
|
||||||
|
import { CommonActions, StackActions } from '@react-navigation/native';
|
||||||
|
|
||||||
let _navigator;
|
const navigationRef = React.createRef();
|
||||||
|
const routeNameRef = React.createRef();
|
||||||
|
|
||||||
function setTopLevelNavigator(navigatorRef) {
|
function navigate(name, params) {
|
||||||
_navigator = navigatorRef;
|
navigationRef.current?.navigate(name, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
function back() {
|
function back() {
|
||||||
_navigator.dispatch(
|
navigationRef.current?.dispatch(CommonActions.goBack());
|
||||||
NavigationActions.back()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigate(routeName, params) {
|
function replace(name, params) {
|
||||||
_navigator.dispatch(
|
navigationRef.current?.dispatch(StackActions.replace(name, params));
|
||||||
NavigationActions.navigate({
|
|
||||||
routeName,
|
|
||||||
params
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
back,
|
navigationRef,
|
||||||
|
routeNameRef,
|
||||||
navigate,
|
navigate,
|
||||||
setTopLevelNavigator
|
back,
|
||||||
|
replace
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,9 @@
|
||||||
import { NavigationActions } from 'react-navigation';
|
import { createRef } from 'react';
|
||||||
|
|
||||||
let _shareNavigator;
|
const navigationRef = createRef();
|
||||||
|
const routeNameRef = createRef();
|
||||||
function setTopLevelNavigator(navigatorRef) {
|
|
||||||
_shareNavigator = navigatorRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate(routeName, params) {
|
|
||||||
_shareNavigator.dispatch(
|
|
||||||
NavigationActions.navigate({
|
|
||||||
routeName,
|
|
||||||
params
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
navigate,
|
navigationRef,
|
||||||
setTopLevelNavigator
|
routeNameRef
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,14 @@ async function load({ rid: roomId, latest, t }) {
|
||||||
if (latest) {
|
if (latest) {
|
||||||
params = { ...params, latest: new Date(latest).toISOString() };
|
params = { ...params, latest: new Date(latest).toISOString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const apiType = this.roomTypeToApiType(t);
|
||||||
|
if (!apiType) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
// RC 0.48.0
|
// RC 0.48.0
|
||||||
const data = await this.sdk.get(`${ this.roomTypeToApiType(t) }.history`, params);
|
const data = await this.sdk.get(`${ apiType }.history`, params);
|
||||||
if (!data || data.status === 'error') {
|
if (!data || data.status === 'error') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@ import sharedStyles from '../../views/Styles';
|
||||||
import { ROW_HEIGHT } from '../../presentation/RoomItem';
|
import { ROW_HEIGHT } from '../../presentation/RoomItem';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import { getActiveRoute } from '../../utils/navigation';
|
||||||
|
import Navigation from '../../lib/Navigation';
|
||||||
|
import { goRoom } from '../../utils/goRoom';
|
||||||
|
|
||||||
const AVATAR_SIZE = 48;
|
const AVATAR_SIZE = 48;
|
||||||
const ANIMATION_DURATION = 300;
|
const ANIMATION_DURATION = 300;
|
||||||
|
@ -71,7 +74,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
class NotificationBadge extends React.Component {
|
class NotificationBadge extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
isMasterDetail: PropTypes.bool,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
notification: PropTypes.object,
|
notification: PropTypes.object,
|
||||||
|
@ -102,16 +105,20 @@ class NotificationBadge extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate(prevProps) {
|
||||||
const { notification: { payload }, navigation } = this.props;
|
const { notification: { payload } } = this.props;
|
||||||
const navState = this.getNavState(navigation.state);
|
const { notification: { payload: prevPayload } } = prevProps;
|
||||||
|
if (!equal(prevPayload, payload)) {
|
||||||
|
const state = Navigation.navigationRef.current.getRootState();
|
||||||
|
const route = getActiveRoute(state);
|
||||||
if (payload.rid) {
|
if (payload.rid) {
|
||||||
if (navState && navState.routeName === 'RoomView' && navState.params && navState.params.rid === payload.rid) {
|
if (route?.name === 'RoomView' && route.params?.rid === payload.rid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.clearTimeout();
|
this.clearTimeout();
|
||||||
|
@ -150,15 +157,8 @@ class NotificationBadge extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getNavState = (routes) => {
|
goToRoom = () => {
|
||||||
if (!routes.routes) {
|
const { notification, isMasterDetail, baseUrl } = this.props;
|
||||||
return routes;
|
|
||||||
}
|
|
||||||
return this.getNavState(routes.routes[routes.index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
goToRoom = async() => {
|
|
||||||
const { notification, navigation, baseUrl } = this.props;
|
|
||||||
const { payload } = notification;
|
const { payload } = notification;
|
||||||
const { rid, type, prid } = payload;
|
const { rid, type, prid } = payload;
|
||||||
if (!rid) {
|
if (!rid) {
|
||||||
|
@ -167,10 +167,13 @@ class NotificationBadge extends React.Component {
|
||||||
const name = type === 'd' ? payload.sender.username : payload.name;
|
const name = type === 'd' ? payload.sender.username : payload.name;
|
||||||
// if sub is not on local database, title will be null, so we use payload from notification
|
// if sub is not on local database, title will be null, so we use payload from notification
|
||||||
const { title = name } = notification;
|
const { title = name } = notification;
|
||||||
await navigation.navigate('RoomsListView');
|
const item = {
|
||||||
navigation.navigate('RoomView', {
|
|
||||||
rid, name: title, t: type, prid, baseUrl
|
rid, name: title, t: type, prid, baseUrl
|
||||||
});
|
};
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
}
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +237,8 @@ class NotificationBadge extends React.Component {
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
notification: state.notification
|
notification: state.notification,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -25,7 +25,8 @@ class Touchable extends React.Component {
|
||||||
toggleRead: PropTypes.func,
|
toggleRead: PropTypes.func,
|
||||||
hideChannel: PropTypes.func,
|
hideChannel: PropTypes.func,
|
||||||
children: PropTypes.element,
|
children: PropTypes.element,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
isFocused: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -167,7 +168,7 @@ class Touchable extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
testID, isRead, width, favorite, children, theme
|
testID, isRead, width, favorite, children, theme, isFocused
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -203,7 +204,7 @@ class Touchable extends React.Component {
|
||||||
theme={theme}
|
theme={theme}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: themes[theme].backgroundColor
|
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -28,7 +28,8 @@ const attrs = [
|
||||||
'favorite',
|
'favorite',
|
||||||
'status',
|
'status',
|
||||||
'connected',
|
'connected',
|
||||||
'theme'
|
'theme',
|
||||||
|
'isFocused'
|
||||||
];
|
];
|
||||||
|
|
||||||
const arePropsEqual = (oldProps, newProps) => {
|
const arePropsEqual = (oldProps, newProps) => {
|
||||||
|
@ -41,7 +42,39 @@ const arePropsEqual = (oldProps, newProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomItem = React.memo(({
|
const RoomItem = React.memo(({
|
||||||
onPress, width, favorite, toggleFav, isRead, rid, toggleRead, hideChannel, testID, unread, userMentions, name, _updatedAt, alert, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, hideUnreadStatus, lastMessage, status, avatar, useRealName, getUserPresence, isGroupChat, connected, theme
|
onPress,
|
||||||
|
width,
|
||||||
|
favorite,
|
||||||
|
toggleFav,
|
||||||
|
isRead,
|
||||||
|
rid,
|
||||||
|
toggleRead,
|
||||||
|
hideChannel,
|
||||||
|
testID,
|
||||||
|
unread,
|
||||||
|
userMentions,
|
||||||
|
name,
|
||||||
|
_updatedAt,
|
||||||
|
alert,
|
||||||
|
type,
|
||||||
|
avatarSize,
|
||||||
|
baseUrl,
|
||||||
|
userId,
|
||||||
|
username,
|
||||||
|
token,
|
||||||
|
id,
|
||||||
|
prid,
|
||||||
|
showLastMessage,
|
||||||
|
hideUnreadStatus,
|
||||||
|
lastMessage,
|
||||||
|
status,
|
||||||
|
avatar,
|
||||||
|
useRealName,
|
||||||
|
getUserPresence,
|
||||||
|
isGroupChat,
|
||||||
|
connected,
|
||||||
|
theme,
|
||||||
|
isFocused
|
||||||
}) => {
|
}) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (connected && type === 'd' && id) {
|
if (connected && type === 'd' && id) {
|
||||||
|
@ -79,6 +112,7 @@ const RoomItem = React.memo(({
|
||||||
testID={testID}
|
testID={testID}
|
||||||
type={type}
|
type={type}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
isFocused={isFocused}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
|
@ -200,7 +234,8 @@ RoomItem.propTypes = {
|
||||||
getUserPresence: PropTypes.func,
|
getUserPresence: PropTypes.func,
|
||||||
connected: PropTypes.bool,
|
connected: PropTypes.bool,
|
||||||
isGroupChat: PropTypes.bool,
|
isGroupChat: PropTypes.bool,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
isFocused: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
RoomItem.defaultProps = {
|
RoomItem.defaultProps = {
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { APP, APP_STATE } from '../actions/actionsTypes';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
root: null,
|
root: null,
|
||||||
|
isMasterDetail: false,
|
||||||
|
text: null,
|
||||||
ready: false,
|
ready: false,
|
||||||
foreground: true,
|
foreground: true,
|
||||||
background: false
|
background: false
|
||||||
|
@ -24,7 +26,8 @@ export default function app(state = initialState, action) {
|
||||||
case APP.START:
|
case APP.START:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
root: action.root
|
root: action.root,
|
||||||
|
text: action.text
|
||||||
};
|
};
|
||||||
case APP.INIT:
|
case APP.INIT:
|
||||||
return {
|
return {
|
||||||
|
@ -36,6 +39,11 @@ export default function app(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
ready: true
|
ready: true
|
||||||
};
|
};
|
||||||
|
case APP.SET_MASTER_DETAIL:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isMasterDetail: action.isMasterDetail
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default function(state = initialState, action) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rooms: state.rooms
|
rooms: state.rooms
|
||||||
.filter(room => room.rid === action.rid)
|
.filter(rid => rid !== action.rid)
|
||||||
};
|
};
|
||||||
case ROOM.LEAVE:
|
case ROOM.LEAVE:
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -7,7 +7,8 @@ const initialState = {
|
||||||
server: '',
|
server: '',
|
||||||
version: null,
|
version: null,
|
||||||
loading: true,
|
loading: true,
|
||||||
adding: false
|
adding: false,
|
||||||
|
previousServer: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,12 +55,14 @@ export default function server(state = initialState, action) {
|
||||||
case SERVER.INIT_ADD:
|
case SERVER.INIT_ADD:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
adding: true
|
adding: true,
|
||||||
|
previousServer: action.previousServer
|
||||||
};
|
};
|
||||||
case SERVER.FINISH_ADD:
|
case SERVER.FINISH_ADD:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
adding: false
|
adding: false,
|
||||||
|
previousServer: null
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import RocketChat from '../lib/rocketchat';
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
import { goRoom } from '../utils/goRoom';
|
||||||
|
|
||||||
const createChannel = function createChannel(data) {
|
const createChannel = function createChannel(data) {
|
||||||
return RocketChat.createChannel(data);
|
return RocketChat.createChannel(data);
|
||||||
|
@ -55,9 +56,12 @@ const handleRequest = function* handleRequest({ data }) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSuccess = function handleSuccess({ data }) {
|
const handleSuccess = function* handleSuccess({ data }) {
|
||||||
const { rid, t } = data;
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
Navigation.navigate('RoomView', { rid, t, name: RocketChat.getRoomTitle(data) });
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
}
|
||||||
|
goRoom({ item: data, isMasterDetail });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFailure = function handleFailure({ err }) {
|
const handleFailure = function handleFailure({ err }) {
|
||||||
|
|
|
@ -10,8 +10,9 @@ import { inviteLinksSetToken, inviteLinksRequest } from '../actions/inviteLinks'
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
import { appStart } from '../actions';
|
import { appStart, ROOT_INSIDE } from '../actions/app';
|
||||||
import { localAuthenticate } from '../utils/localAuthentication';
|
import { localAuthenticate } from '../utils/localAuthentication';
|
||||||
|
import { goRoom } from '../utils/goRoom';
|
||||||
|
|
||||||
const roomTypes = {
|
const roomTypes = {
|
||||||
channel: 'c', direct: 'd', group: 'p', channels: 'l'
|
channel: 'c', direct: 'd', group: 'p', channels: 'l'
|
||||||
|
@ -29,19 +30,25 @@ const handleInviteLink = function* handleInviteLink({ params, requireLogin = fal
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigate = function* navigate({ params }) {
|
const navigate = function* navigate({ params }) {
|
||||||
yield put(appStart('inside'));
|
yield put(appStart({ root: ROOT_INSIDE }));
|
||||||
if (params.path) {
|
if (params.path) {
|
||||||
const [type, name] = params.path.split('/');
|
const [type, name] = params.path.split('/');
|
||||||
if (type !== 'invite') {
|
if (type !== 'invite') {
|
||||||
const room = yield RocketChat.canOpenRoom(params);
|
const room = yield RocketChat.canOpenRoom(params);
|
||||||
if (room) {
|
if (room) {
|
||||||
yield Navigation.navigate('RoomsListView');
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
Navigation.navigate('RoomView', {
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
|
Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
|
const item = {
|
||||||
name,
|
name,
|
||||||
t: roomTypes[type],
|
t: roomTypes[type],
|
||||||
roomUserId: RocketChat.getUidDirectMessage(room),
|
roomUserId: RocketChat.getUidDirectMessage(room),
|
||||||
...room
|
...room
|
||||||
});
|
};
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
yield handleInviteLink({ params });
|
yield handleInviteLink({ params });
|
||||||
|
|
|
@ -4,14 +4,12 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import RNBootSplash from 'react-native-bootsplash';
|
import RNBootSplash from 'react-native-bootsplash';
|
||||||
import AsyncStorage from '@react-native-community/async-storage';
|
import AsyncStorage from '@react-native-community/async-storage';
|
||||||
|
|
||||||
import * as actions from '../actions';
|
|
||||||
import { selectServerRequest } from '../actions/server';
|
import { selectServerRequest } from '../actions/server';
|
||||||
import { setAllPreferences } from '../actions/sortPreferences';
|
import { setAllPreferences } from '../actions/sortPreferences';
|
||||||
import { toggleCrashReport } from '../actions/crashReport';
|
import { toggleCrashReport } from '../actions/crashReport';
|
||||||
import { APP } from '../actions/actionsTypes';
|
import { APP } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import Navigation from '../lib/Navigation';
|
|
||||||
import {
|
import {
|
||||||
SERVERS, SERVER_ICON, SERVER_NAME, SERVER_URL, TOKEN, USER_ID
|
SERVERS, SERVER_ICON, SERVER_NAME, SERVER_URL, TOKEN, USER_ID
|
||||||
} from '../constants/userDefaults';
|
} from '../constants/userDefaults';
|
||||||
|
@ -19,6 +17,7 @@ import { isIOS } from '../utils/deviceInfo';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import protectedFunction from '../lib/methods/helpers/protectedFunction';
|
import protectedFunction from '../lib/methods/helpers/protectedFunction';
|
||||||
import { localAuthenticate } from '../utils/localAuthentication';
|
import { localAuthenticate } from '../utils/localAuthentication';
|
||||||
|
import { appStart, ROOT_OUTSIDE, appReady } from '../actions/app';
|
||||||
|
|
||||||
export const initLocalSettings = function* initLocalSettings() {
|
export const initLocalSettings = function* initLocalSettings() {
|
||||||
const sortPreferences = yield RocketChat.getSortPreferences();
|
const sortPreferences = yield RocketChat.getSortPreferences();
|
||||||
|
@ -96,7 +95,7 @@ const restore = function* restore() {
|
||||||
RNUserDefaults.clear(RocketChat.TOKEN_KEY),
|
RNUserDefaults.clear(RocketChat.TOKEN_KEY),
|
||||||
RNUserDefaults.clear('currentServer')
|
RNUserDefaults.clear('currentServer')
|
||||||
]);
|
]);
|
||||||
yield put(actions.appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
} else {
|
} else {
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
const serverCollections = serversDB.collections.get('servers');
|
const serverCollections = serversDB.collections.get('servers');
|
||||||
|
@ -106,23 +105,14 @@ const restore = function* restore() {
|
||||||
yield put(selectServerRequest(server, serverObj && serverObj.version));
|
yield put(selectServerRequest(server, serverObj && serverObj.version));
|
||||||
}
|
}
|
||||||
|
|
||||||
yield put(actions.appReady({}));
|
yield put(appReady({}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
yield put(actions.appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const start = function* start({ root, text }) {
|
const start = function start() {
|
||||||
if (root === 'inside') {
|
|
||||||
yield Navigation.navigate('InsideStack');
|
|
||||||
} else if (root === 'setUsername') {
|
|
||||||
yield Navigation.navigate('SetUsernameStack');
|
|
||||||
} else if (root === 'outside') {
|
|
||||||
yield Navigation.navigate('OutsideStack');
|
|
||||||
} else if (root === 'loading') {
|
|
||||||
yield Navigation.navigate('AuthLoading', { text });
|
|
||||||
}
|
|
||||||
RNBootSplash.hide();
|
RNBootSplash.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,9 @@ import moment from 'moment';
|
||||||
import 'moment/min/locales';
|
import 'moment/min/locales';
|
||||||
|
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
import { appStart } from '../actions';
|
import {
|
||||||
|
appStart, ROOT_SET_USERNAME, ROOT_INSIDE, ROOT_LOADING, ROOT_OUTSIDE
|
||||||
|
} from '../actions/app';
|
||||||
import { serverFinishAdd, selectServerRequest } from '../actions/server';
|
import { serverFinishAdd, selectServerRequest } from '../actions/server';
|
||||||
import {
|
import {
|
||||||
loginFailure, loginSuccess, setUser, logout
|
loginFailure, loginSuccess, setUser, logout
|
||||||
|
@ -40,7 +42,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
|
||||||
if (!result.username) {
|
if (!result.username) {
|
||||||
yield put(serverFinishAdd());
|
yield put(serverFinishAdd());
|
||||||
yield put(setUser(result));
|
yield put(setUser(result));
|
||||||
yield put(appStart('setUsername'));
|
yield put(appStart({ root: ROOT_SET_USERNAME }));
|
||||||
} else {
|
} else {
|
||||||
const server = yield select(getServer);
|
const server = yield select(getServer);
|
||||||
yield localAuthenticate(server);
|
yield localAuthenticate(server);
|
||||||
|
@ -133,17 +135,17 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
|
||||||
let currentRoot;
|
let currentRoot;
|
||||||
if (adding) {
|
if (adding) {
|
||||||
yield put(serverFinishAdd());
|
yield put(serverFinishAdd());
|
||||||
yield put(appStart('inside'));
|
yield put(appStart({ root: ROOT_INSIDE }));
|
||||||
} else {
|
} else {
|
||||||
currentRoot = yield select(state => state.app.root);
|
currentRoot = yield select(state => state.app.root);
|
||||||
if (currentRoot !== 'inside') {
|
if (currentRoot !== ROOT_INSIDE) {
|
||||||
yield put(appStart('inside'));
|
yield put(appStart({ root: ROOT_INSIDE }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// after a successful login, check if it's been invited via invite link
|
// after a successful login, check if it's been invited via invite link
|
||||||
currentRoot = yield select(state => state.app.root);
|
currentRoot = yield select(state => state.app.root);
|
||||||
if (currentRoot === 'inside') {
|
if (currentRoot === ROOT_INSIDE) {
|
||||||
const inviteLinkToken = yield select(state => state.inviteLinks.token);
|
const inviteLinkToken = yield select(state => state.inviteLinks.token);
|
||||||
if (inviteLinkToken) {
|
if (inviteLinkToken) {
|
||||||
yield put(inviteLinksRequest(inviteLinkToken));
|
yield put(inviteLinksRequest(inviteLinkToken));
|
||||||
|
@ -155,7 +157,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogout = function* handleLogout({ forcedByServer }) {
|
const handleLogout = function* handleLogout({ forcedByServer }) {
|
||||||
yield put(appStart('loading', I18n.t('Logging_out')));
|
yield put(appStart({ root: ROOT_LOADING, text: I18n.t('Logging_out') }));
|
||||||
const server = yield select(getServer);
|
const server = yield select(getServer);
|
||||||
if (server) {
|
if (server) {
|
||||||
try {
|
try {
|
||||||
|
@ -163,7 +165,7 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
||||||
|
|
||||||
// if the user was logged out by the server
|
// if the user was logged out by the server
|
||||||
if (forcedByServer) {
|
if (forcedByServer) {
|
||||||
yield put(appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
showErrorAlert(I18n.t('Logged_out_by_server'), I18n.t('Oops'));
|
showErrorAlert(I18n.t('Logged_out_by_server'), I18n.t('Oops'));
|
||||||
EventEmitter.emit('NewServer', { server });
|
EventEmitter.emit('NewServer', { server });
|
||||||
} else {
|
} else {
|
||||||
|
@ -183,10 +185,10 @@ const handleLogout = function* handleLogout({ forcedByServer }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if there's no servers, go outside
|
// if there's no servers, go outside
|
||||||
yield put(appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield put(appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { takeLatest } from 'redux-saga/effects';
|
import { takeLatest, select } from 'redux-saga/effects';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
|
@ -6,32 +6,28 @@ import { MESSAGES } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
import { goRoom } from '../utils/goRoom';
|
||||||
const goRoom = function goRoom({
|
|
||||||
rid, name, fname, message
|
|
||||||
}) {
|
|
||||||
Navigation.navigate('RoomsListView');
|
|
||||||
Navigation.navigate('RoomView', {
|
|
||||||
rid, name, fname, t: 'd', message
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
const handleReplyBroadcast = function* handleReplyBroadcast({ message }) {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const { username, name } = message.u;
|
const { username } = message.u;
|
||||||
const subsCollection = db.collections.get('subscriptions');
|
const subsCollection = db.collections.get('subscriptions');
|
||||||
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
|
const subscriptions = yield subsCollection.query(Q.where('name', username)).fetch();
|
||||||
|
|
||||||
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
|
Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
|
|
||||||
if (subscriptions.length) {
|
if (subscriptions.length) {
|
||||||
yield goRoom({
|
goRoom({ item: subscriptions[0], isMasterDetail, message });
|
||||||
rid: subscriptions[0].rid, name: username, fname: name, message
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
const result = yield RocketChat.createDirectMessage(username);
|
const result = yield RocketChat.createDirectMessage(username);
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
yield goRoom({
|
goRoom({ item: result?.room, isMasterDetail, message });
|
||||||
rid: result?.room.rid, t: 'd', name: username, fname: name, message
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -31,7 +31,12 @@ const watchUserTyping = function* watchUserTyping({ rid, status }) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemovedRoom = function* handleRemovedRoom() {
|
const handleRemovedRoom = function* handleRemovedRoom() {
|
||||||
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
|
if (isMasterDetail) {
|
||||||
|
yield Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
yield Navigation.navigate('RoomsListView');
|
yield Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
// types.ROOM.REMOVE is triggered by `subscriptions-changed` with `removed` arg
|
// types.ROOM.REMOVE is triggered by `subscriptions-changed` with `removed` arg
|
||||||
const { timeout } = yield race({
|
const { timeout } = yield race({
|
||||||
deleteFinished: take(types.ROOM.REMOVED),
|
deleteFinished: take(types.ROOM.REMOVED),
|
||||||
|
@ -74,7 +79,12 @@ const handleCloseRoom = function* handleCloseRoom({ rid }) {
|
||||||
const closeRoom = async(comment = '') => {
|
const closeRoom = async(comment = '') => {
|
||||||
try {
|
try {
|
||||||
await RocketChat.closeLivechat(rid, comment);
|
await RocketChat.closeLivechat(rid, comment);
|
||||||
|
const isMasterDetail = await select(state => state.app.isMasterDetail);
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
Navigation.navigate('RoomsListView');
|
Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -105,7 +115,12 @@ const handleForwardRoom = function* handleForwardRoom({ transferData }) {
|
||||||
try {
|
try {
|
||||||
const result = yield RocketChat.forwardLivechat(transferData);
|
const result = yield RocketChat.forwardLivechat(transferData);
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
|
const isMasterDetail = yield select(state => state.app.isMasterDetail);
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
Navigation.navigate('RoomsListView');
|
Navigation.navigate('RoomsListView');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showErrorAlert(I18n.t('No_available_agents_to_transfer'), I18n.t('Oops'));
|
showErrorAlert(I18n.t('No_available_agents_to_transfer'), I18n.t('Oops'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import semver from 'semver';
|
||||||
|
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
import { SERVER } from '../actions/actionsTypes';
|
import { SERVER } from '../actions/actionsTypes';
|
||||||
import * as actions from '../actions';
|
|
||||||
import {
|
import {
|
||||||
serverFailure, selectServerRequest, selectServerSuccess, selectServerFailure
|
serverFailure, selectServerRequest, selectServerSuccess, selectServerFailure
|
||||||
} from '../actions/server';
|
} from '../actions/server';
|
||||||
|
@ -19,6 +18,7 @@ import { extractHostname } from '../utils/server';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { SERVERS, TOKEN, SERVER_URL } from '../constants/userDefaults';
|
import { SERVERS, TOKEN, SERVER_URL } from '../constants/userDefaults';
|
||||||
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';
|
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';
|
||||||
|
import { appStart, ROOT_INSIDE, ROOT_OUTSIDE } from '../actions/app';
|
||||||
|
|
||||||
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
||||||
try {
|
try {
|
||||||
|
@ -103,10 +103,10 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
||||||
yield put(clearSettings());
|
yield put(clearSettings());
|
||||||
yield RocketChat.connect({ server, user, logoutOnError: true });
|
yield RocketChat.connect({ server, user, logoutOnError: true });
|
||||||
yield put(setUser(user));
|
yield put(setUser(user));
|
||||||
yield put(actions.appStart('inside'));
|
yield put(appStart({ root: ROOT_INSIDE }));
|
||||||
} else {
|
} else {
|
||||||
yield RocketChat.connect({ server });
|
yield RocketChat.connect({ server });
|
||||||
yield put(actions.appStart('outside'));
|
yield put(appStart({ root: ROOT_OUTSIDE }));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't use yield here because fetch of Settings & Custom Emojis is slower
|
// We can't use yield here because fetch of Settings & Custom Emojis is slower
|
||||||
|
|
|
@ -5,10 +5,11 @@ import { setBadgeCount } from '../notifications/push';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import { localAuthenticate, saveLastLocalAuthenticationSession } from '../utils/localAuthentication';
|
import { localAuthenticate, saveLastLocalAuthenticationSession } from '../utils/localAuthentication';
|
||||||
import { APP_STATE } from '../actions/actionsTypes';
|
import { APP_STATE } from '../actions/actionsTypes';
|
||||||
|
import { ROOT_OUTSIDE } from '../actions/app';
|
||||||
|
|
||||||
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
const appRoot = yield select(state => state.app.root);
|
const appRoot = yield select(state => state.app.root);
|
||||||
if (appRoot === 'outside') {
|
if (appRoot === ROOT_OUTSIDE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
|
@ -27,7 +28,7 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
|
|
||||||
const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
||||||
const appRoot = yield select(state => state.app.root);
|
const appRoot = yield select(state => state.app.root);
|
||||||
if (appRoot === 'outside') {
|
if (appRoot === ROOT_OUTSIDE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
|
|
180
app/share.js
180
app/share.js
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View } from 'react-native';
|
import PropTypes from 'prop-types';
|
||||||
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
import { AppearanceProvider } from 'react-native-appearance';
|
import { AppearanceProvider } from 'react-native-appearance';
|
||||||
import { createStackNavigator } from 'react-navigation-stack';
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import RNUserDefaults from 'rn-user-defaults';
|
import RNUserDefaults from 'rn-user-defaults';
|
||||||
|
|
||||||
|
@ -14,61 +14,114 @@ import {
|
||||||
} from './utils/theme';
|
} from './utils/theme';
|
||||||
import Navigation from './lib/ShareNavigation';
|
import Navigation from './lib/ShareNavigation';
|
||||||
import store from './lib/createStore';
|
import store from './lib/createStore';
|
||||||
import sharedStyles from './views/Styles';
|
import { supportSystemTheme } from './utils/deviceInfo';
|
||||||
import { hasNotch, supportSystemTheme } from './utils/deviceInfo';
|
import {
|
||||||
import { defaultHeader, onNavigationStateChange, cardStyle } from './utils/navigation';
|
defaultHeader, themedHeader, getActiveRouteName, navigationTheme
|
||||||
|
} from './utils/navigation';
|
||||||
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat';
|
||||||
import { ThemeContext } from './theme';
|
import { ThemeContext } from './theme';
|
||||||
import { localAuthenticate } from './utils/localAuthentication';
|
import { localAuthenticate } from './utils/localAuthentication';
|
||||||
import ScreenLockedView from './views/ScreenLockedView';
|
import ScreenLockedView from './views/ScreenLockedView';
|
||||||
|
|
||||||
const InsideNavigator = createStackNavigator({
|
// Outside Stack
|
||||||
ShareListView: {
|
import WithoutServersView from './views/WithoutServersView';
|
||||||
getScreen: () => require('./views/ShareListView').default
|
|
||||||
},
|
|
||||||
ShareView: {
|
|
||||||
getScreen: () => require('./views/ShareView').default
|
|
||||||
},
|
|
||||||
SelectServerView: {
|
|
||||||
getScreen: () => require('./views/SelectServerView').default
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
initialRouteName: 'ShareListView',
|
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const OutsideNavigator = createStackNavigator({
|
// Inside Stack
|
||||||
WithoutServersView: {
|
import ShareListView from './views/ShareListView';
|
||||||
getScreen: () => require('./views/WithoutServersView').default
|
import ShareView from './views/ShareView';
|
||||||
}
|
import SelectServerView from './views/SelectServerView';
|
||||||
}, {
|
import { setCurrentScreen } from './utils/log';
|
||||||
initialRouteName: 'WithoutServersView',
|
import AuthLoadingView from './views/AuthLoadingView';
|
||||||
defaultNavigationOptions: defaultHeader,
|
|
||||||
cardStyle
|
|
||||||
});
|
|
||||||
|
|
||||||
const AppContainer = createAppContainer(createSwitchNavigator({
|
const Inside = createStackNavigator();
|
||||||
OutsideStack: OutsideNavigator,
|
const InsideStack = () => {
|
||||||
InsideStack: InsideNavigator,
|
const { theme } = useContext(ThemeContext);
|
||||||
AuthLoading: {
|
|
||||||
getScreen: () => require('./views/AuthLoadingView').default
|
const screenOptions = {
|
||||||
}
|
...defaultHeader,
|
||||||
},
|
...themedHeader(theme)
|
||||||
{
|
};
|
||||||
initialRouteName: 'AuthLoading'
|
screenOptions.headerStyle = {
|
||||||
}));
|
...screenOptions.headerStyle,
|
||||||
|
// TODO: fix on multiple files PR :)
|
||||||
|
height: 57
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Inside.Navigator screenOptions={screenOptions}>
|
||||||
|
<Inside.Screen
|
||||||
|
name='ShareListView'
|
||||||
|
component={ShareListView}
|
||||||
|
/>
|
||||||
|
<Inside.Screen
|
||||||
|
name='ShareView'
|
||||||
|
component={ShareView}
|
||||||
|
/>
|
||||||
|
<Inside.Screen
|
||||||
|
name='SelectServerView'
|
||||||
|
component={SelectServerView}
|
||||||
|
options={SelectServerView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</Inside.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Outside = createStackNavigator();
|
||||||
|
const OutsideStack = () => {
|
||||||
|
const { theme } = useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme) }}>
|
||||||
|
<Outside.Screen
|
||||||
|
name='WithoutServersView'
|
||||||
|
component={WithoutServersView}
|
||||||
|
options={WithoutServersView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</Outside.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// App
|
||||||
|
const Stack = createStackNavigator();
|
||||||
|
export const App = ({ root }) => (
|
||||||
|
<Stack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
|
<>
|
||||||
|
{!root ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='AuthLoading'
|
||||||
|
component={AuthLoadingView}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === 'outside' ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='OutsideStack'
|
||||||
|
component={OutsideStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{root === 'inside' ? (
|
||||||
|
<Stack.Screen
|
||||||
|
name='InsideStack'
|
||||||
|
component={InsideStack}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
</Stack.Navigator>
|
||||||
|
);
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
root: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
class Root extends React.Component {
|
class Root extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isLandscape: false,
|
|
||||||
theme: defaultTheme(),
|
theme: defaultTheme(),
|
||||||
themePreferences: {
|
themePreferences: {
|
||||||
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
currentTheme: supportSystemTheme() ? 'automatic' : 'light',
|
||||||
darkLevel: 'dark'
|
darkLevel: 'dark'
|
||||||
}
|
},
|
||||||
|
root: ''
|
||||||
};
|
};
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
@ -85,11 +138,16 @@ class Root extends React.Component {
|
||||||
|
|
||||||
if (currentServer && token) {
|
if (currentServer && token) {
|
||||||
await localAuthenticate(currentServer);
|
await localAuthenticate(currentServer);
|
||||||
await Navigation.navigate('InsideStack');
|
this.setState({ root: 'inside' });
|
||||||
await RocketChat.shareExtensionInit(currentServer);
|
await RocketChat.shareExtensionInit(currentServer);
|
||||||
} else {
|
} else {
|
||||||
await Navigation.navigate('OutsideStack');
|
this.setState({ root: 'outside' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const state = Navigation.navigationRef.current.getRootState();
|
||||||
|
const currentRouteName = getActiveRouteName(state);
|
||||||
|
Navigation.routeNameRef.current = currentRouteName;
|
||||||
|
setCurrentScreen(currentRouteName);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme = (newTheme = {}) => {
|
setTheme = (newTheme = {}) => {
|
||||||
|
@ -101,32 +159,30 @@ class Root extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLayout = (event) => {
|
|
||||||
const { width, height } = event.nativeEvent.layout;
|
|
||||||
this.setState({ isLandscape: width > height });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isLandscape, theme } = this.state;
|
const { theme, root } = this.state;
|
||||||
|
const navTheme = navigationTheme(theme);
|
||||||
return (
|
return (
|
||||||
<AppearanceProvider>
|
<AppearanceProvider>
|
||||||
<View
|
|
||||||
style={[sharedStyles.container, isLandscape && hasNotch ? sharedStyles.notchLandscapeContainer : {}]}
|
|
||||||
onLayout={this.handleLayout}
|
|
||||||
>
|
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ThemeContext.Provider value={{ theme }}>
|
<ThemeContext.Provider value={{ theme }}>
|
||||||
<AppContainer
|
<NavigationContainer
|
||||||
ref={(navigatorRef) => {
|
theme={navTheme}
|
||||||
Navigation.setTopLevelNavigator(navigatorRef);
|
ref={Navigation.navigationRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
const previousRouteName = Navigation.routeNameRef.current;
|
||||||
|
const currentRouteName = getActiveRouteName(state);
|
||||||
|
if (previousRouteName !== currentRouteName) {
|
||||||
|
setCurrentScreen(currentRouteName);
|
||||||
|
}
|
||||||
|
Navigation.routeNameRef.current = currentRouteName;
|
||||||
}}
|
}}
|
||||||
onNavigationStateChange={onNavigationStateChange}
|
>
|
||||||
screenProps={{ theme }}
|
<App root={root} />
|
||||||
/>
|
</NavigationContainer>
|
||||||
<ScreenLockedView />
|
<ScreenLockedView />
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</View>
|
|
||||||
</AppearanceProvider>
|
</AppearanceProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
19
app/split.js
19
app/split.js
|
@ -1,19 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
||||||
|
|
||||||
import { isTablet } from './utils/deviceInfo';
|
|
||||||
|
|
||||||
export const SplitContext = React.createContext(null);
|
|
||||||
|
|
||||||
export function withSplit(Component) {
|
|
||||||
if (isTablet) {
|
|
||||||
const SplitComponent = props => (
|
|
||||||
<SplitContext.Consumer>
|
|
||||||
{contexts => <Component {...props} {...contexts} />}
|
|
||||||
</SplitContext.Consumer>
|
|
||||||
);
|
|
||||||
hoistNonReactStatics(SplitComponent, Component);
|
|
||||||
return SplitComponent;
|
|
||||||
}
|
|
||||||
return Component;
|
|
||||||
}
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||||
|
|
||||||
|
import { ThemeContext } from '../theme';
|
||||||
|
import {
|
||||||
|
defaultHeader, themedHeader, ModalAnimation, StackAnimation
|
||||||
|
} from '../utils/navigation';
|
||||||
|
import Toast from '../containers/Toast';
|
||||||
|
import Sidebar from '../views/SidebarView';
|
||||||
|
import NotificationBadge from '../notifications/inApp';
|
||||||
|
|
||||||
|
// Chats Stack
|
||||||
|
import RoomView from '../views/RoomView';
|
||||||
|
import RoomsListView from '../views/RoomsListView';
|
||||||
|
import RoomActionsView from '../views/RoomActionsView';
|
||||||
|
import RoomInfoView from '../views/RoomInfoView';
|
||||||
|
import RoomInfoEditView from '../views/RoomInfoEditView';
|
||||||
|
import RoomMembersView from '../views/RoomMembersView';
|
||||||
|
import SearchMessagesView from '../views/SearchMessagesView';
|
||||||
|
import SelectedUsersView from '../views/SelectedUsersView';
|
||||||
|
import InviteUsersView from '../views/InviteUsersView';
|
||||||
|
import InviteUsersEditView from '../views/InviteUsersEditView';
|
||||||
|
import MessagesView from '../views/MessagesView';
|
||||||
|
import AutoTranslateView from '../views/AutoTranslateView';
|
||||||
|
import DirectoryView from '../views/DirectoryView';
|
||||||
|
import NotificationPrefView from '../views/NotificationPreferencesView';
|
||||||
|
import VisitorNavigationView from '../views/VisitorNavigationView';
|
||||||
|
import ForwardLivechatView from '../views/ForwardLivechatView';
|
||||||
|
import LivechatEditView from '../views/LivechatEditView';
|
||||||
|
import PickerView from '../views/PickerView';
|
||||||
|
import ThreadMessagesView from '../views/ThreadMessagesView';
|
||||||
|
import MarkdownTableView from '../views/MarkdownTableView';
|
||||||
|
import ReadReceiptsView from '../views/ReadReceiptView';
|
||||||
|
|
||||||
|
// Profile Stack
|
||||||
|
import ProfileView from '../views/ProfileView';
|
||||||
|
|
||||||
|
// Settings Stack
|
||||||
|
import SettingsView from '../views/SettingsView';
|
||||||
|
import LanguageView from '../views/LanguageView';
|
||||||
|
import ThemeView from '../views/ThemeView';
|
||||||
|
import DefaultBrowserView from '../views/DefaultBrowserView';
|
||||||
|
import ScreenLockConfigView from '../views/ScreenLockConfigView';
|
||||||
|
|
||||||
|
// Admin Stack
|
||||||
|
import AdminPanelView from '../views/AdminPanelView';
|
||||||
|
|
||||||
|
// NewMessage Stack
|
||||||
|
import NewMessageView from '../views/NewMessageView';
|
||||||
|
import CreateChannelView from '../views/CreateChannelView';
|
||||||
|
|
||||||
|
// InsideStackNavigator
|
||||||
|
import AttachmentView from '../views/AttachmentView';
|
||||||
|
import ModalBlockView from '../views/ModalBlockView';
|
||||||
|
import JitsiMeetView from '../views/JitsiMeetView';
|
||||||
|
import StatusView from '../views/StatusView';
|
||||||
|
import CreateDiscussionView from '../views/CreateDiscussionView';
|
||||||
|
|
||||||
|
// ChatsStackNavigator
|
||||||
|
const ChatsStack = createStackNavigator();
|
||||||
|
const ChatsStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
return (
|
||||||
|
<ChatsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomsListView'
|
||||||
|
component={RoomsListView}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomView'
|
||||||
|
component={RoomView}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomActionsView'
|
||||||
|
component={RoomActionsView}
|
||||||
|
options={RoomActionsView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomInfoView'
|
||||||
|
component={RoomInfoView}
|
||||||
|
options={RoomInfoView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomInfoEditView'
|
||||||
|
component={RoomInfoEditView}
|
||||||
|
options={RoomInfoEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomMembersView'
|
||||||
|
component={RoomMembersView}
|
||||||
|
options={RoomMembersView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='SearchMessagesView'
|
||||||
|
component={SearchMessagesView}
|
||||||
|
options={SearchMessagesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='SelectedUsersView'
|
||||||
|
component={SelectedUsersView}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='InviteUsersView'
|
||||||
|
component={InviteUsersView}
|
||||||
|
options={InviteUsersView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='InviteUsersEditView'
|
||||||
|
component={InviteUsersEditView}
|
||||||
|
options={InviteUsersEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='MessagesView'
|
||||||
|
component={MessagesView}
|
||||||
|
options={MessagesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='AutoTranslateView'
|
||||||
|
component={AutoTranslateView}
|
||||||
|
options={AutoTranslateView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='DirectoryView'
|
||||||
|
component={DirectoryView}
|
||||||
|
options={DirectoryView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='NotificationPrefView'
|
||||||
|
component={NotificationPrefView}
|
||||||
|
options={NotificationPrefView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='VisitorNavigationView'
|
||||||
|
component={VisitorNavigationView}
|
||||||
|
options={VisitorNavigationView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='ForwardLivechatView'
|
||||||
|
component={ForwardLivechatView}
|
||||||
|
options={ForwardLivechatView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='LivechatEditView'
|
||||||
|
component={LivechatEditView}
|
||||||
|
options={LivechatEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='PickerView'
|
||||||
|
component={PickerView}
|
||||||
|
options={PickerView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='ThreadMessagesView'
|
||||||
|
component={ThreadMessagesView}
|
||||||
|
options={ThreadMessagesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='MarkdownTableView'
|
||||||
|
component={MarkdownTableView}
|
||||||
|
options={MarkdownTableView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='ReadReceiptsView'
|
||||||
|
component={ReadReceiptsView}
|
||||||
|
options={ReadReceiptsView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</ChatsStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ProfileStackNavigator
|
||||||
|
const ProfileStack = createStackNavigator();
|
||||||
|
const ProfileStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
return (
|
||||||
|
<ProfileStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<ProfileStack.Screen
|
||||||
|
name='ProfileView'
|
||||||
|
component={ProfileView}
|
||||||
|
options={ProfileView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</ProfileStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SettingsStackNavigator
|
||||||
|
const SettingsStack = createStackNavigator();
|
||||||
|
const SettingsStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<SettingsStack.Screen
|
||||||
|
name='SettingsView'
|
||||||
|
component={SettingsView}
|
||||||
|
options={SettingsView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<SettingsStack.Screen
|
||||||
|
name='LanguageView'
|
||||||
|
component={LanguageView}
|
||||||
|
options={LanguageView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<SettingsStack.Screen
|
||||||
|
name='ThemeView'
|
||||||
|
component={ThemeView}
|
||||||
|
options={ThemeView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<SettingsStack.Screen
|
||||||
|
name='DefaultBrowserView'
|
||||||
|
component={DefaultBrowserView}
|
||||||
|
options={DefaultBrowserView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<SettingsStack.Screen
|
||||||
|
name='ScreenLockConfigView'
|
||||||
|
component={ScreenLockConfigView}
|
||||||
|
options={ScreenLockConfigView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</SettingsStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// AdminPanelStackNavigator
|
||||||
|
const AdminPanelStack = createStackNavigator();
|
||||||
|
const AdminPanelStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminPanelStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<AdminPanelStack.Screen
|
||||||
|
name='AdminPanelView'
|
||||||
|
component={AdminPanelView}
|
||||||
|
options={AdminPanelView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</AdminPanelStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// DrawerNavigator
|
||||||
|
const Drawer = createDrawerNavigator();
|
||||||
|
const DrawerNavigator = () => (
|
||||||
|
<Drawer.Navigator
|
||||||
|
drawerContent={({ navigation, state }) => <Sidebar navigation={navigation} state={state} />}
|
||||||
|
screenOptions={{ swipeEnabled: false }}
|
||||||
|
drawerType='back'
|
||||||
|
>
|
||||||
|
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />
|
||||||
|
<Drawer.Screen name='ProfileStackNavigator' component={ProfileStackNavigator} />
|
||||||
|
<Drawer.Screen name='SettingsStackNavigator' component={SettingsStackNavigator} />
|
||||||
|
<Drawer.Screen name='AdminPanelStackNavigator' component={AdminPanelStackNavigator} />
|
||||||
|
</Drawer.Navigator>
|
||||||
|
);
|
||||||
|
|
||||||
|
// NewMessageStackNavigator
|
||||||
|
const NewMessageStack = createStackNavigator();
|
||||||
|
const NewMessageStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NewMessageStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<NewMessageStack.Screen
|
||||||
|
name='NewMessageView'
|
||||||
|
component={NewMessageView}
|
||||||
|
options={NewMessageView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<NewMessageStack.Screen
|
||||||
|
name='SelectedUsersViewCreateChannel'
|
||||||
|
component={SelectedUsersView}
|
||||||
|
/>
|
||||||
|
<NewMessageStack.Screen
|
||||||
|
name='CreateChannelView'
|
||||||
|
component={CreateChannelView}
|
||||||
|
options={CreateChannelView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<NewMessageStack.Screen
|
||||||
|
name='CreateDiscussionView'
|
||||||
|
component={CreateDiscussionView}
|
||||||
|
/>
|
||||||
|
</NewMessageStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// InsideStackNavigator
|
||||||
|
const InsideStack = createStackNavigator();
|
||||||
|
const InsideStackNavigator = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InsideStack.Navigator mode='modal' screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...ModalAnimation }}>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='DrawerNavigator'
|
||||||
|
component={DrawerNavigator}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='NewMessageStackNavigator'
|
||||||
|
component={NewMessageStackNavigator}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='AttachmentView'
|
||||||
|
component={AttachmentView}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='StatusView'
|
||||||
|
component={StatusView}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='ModalBlockView'
|
||||||
|
component={ModalBlockView}
|
||||||
|
options={ModalBlockView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='JitsiMeetView'
|
||||||
|
component={JitsiMeetView}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
</InsideStack.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RootInsideStack = ({ navigation, route }) => (
|
||||||
|
<>
|
||||||
|
<InsideStackNavigator navigation={navigation} />
|
||||||
|
<NotificationBadge navigation={navigation} route={route} />
|
||||||
|
<Toast />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
RootInsideStack.propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RootInsideStack;
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { TouchableWithoutFeedback, View, StyleSheet } from 'react-native';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import sharedStyles from '../../views/Styles';
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
root: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center'
|
||||||
|
},
|
||||||
|
backdrop: {
|
||||||
|
...StyleSheet.absoluteFill
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ModalContainer = ({ navigation, children, theme }) => (
|
||||||
|
<View style={[styles.root, { backgroundColor: `${ themes[theme].backdropColor }70` }]}>
|
||||||
|
<TouchableWithoutFeedback onPress={() => navigation.pop()}>
|
||||||
|
<View style={styles.backdrop} />
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
<View style={sharedStyles.modalFormSheet}>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
ModalContainer.propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
|
children: PropTypes.element,
|
||||||
|
theme: PropTypes.string
|
||||||
|
};
|
|
@ -0,0 +1,307 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIsFocused } from '@react-navigation/native';
|
||||||
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||||
|
|
||||||
|
import { ThemeContext } from '../../theme';
|
||||||
|
import {
|
||||||
|
defaultHeader, themedHeader, StackAnimation, FadeFromCenterModal
|
||||||
|
} from '../../utils/navigation';
|
||||||
|
import Toast from '../../containers/Toast';
|
||||||
|
import NotificationBadge from '../../notifications/inApp';
|
||||||
|
import { ModalContainer } from './ModalContainer';
|
||||||
|
|
||||||
|
// Chats Stack
|
||||||
|
import RoomView from '../../views/RoomView';
|
||||||
|
import RoomsListView from '../../views/RoomsListView';
|
||||||
|
import RoomActionsView from '../../views/RoomActionsView';
|
||||||
|
import RoomInfoView from '../../views/RoomInfoView';
|
||||||
|
import RoomInfoEditView from '../../views/RoomInfoEditView';
|
||||||
|
import RoomMembersView from '../../views/RoomMembersView';
|
||||||
|
import SearchMessagesView from '../../views/SearchMessagesView';
|
||||||
|
import SelectedUsersView from '../../views/SelectedUsersView';
|
||||||
|
import InviteUsersView from '../../views/InviteUsersView';
|
||||||
|
import InviteUsersEditView from '../../views/InviteUsersEditView';
|
||||||
|
import MessagesView from '../../views/MessagesView';
|
||||||
|
import AutoTranslateView from '../../views/AutoTranslateView';
|
||||||
|
import DirectoryView from '../../views/DirectoryView';
|
||||||
|
import NotificationPrefView from '../../views/NotificationPreferencesView';
|
||||||
|
import VisitorNavigationView from '../../views/VisitorNavigationView';
|
||||||
|
import ForwardLivechatView from '../../views/ForwardLivechatView';
|
||||||
|
import LivechatEditView from '../../views/LivechatEditView';
|
||||||
|
import PickerView from '../../views/PickerView';
|
||||||
|
import ThreadMessagesView from '../../views/ThreadMessagesView';
|
||||||
|
import MarkdownTableView from '../../views/MarkdownTableView';
|
||||||
|
import ReadReceiptsView from '../../views/ReadReceiptView';
|
||||||
|
import ProfileView from '../../views/ProfileView';
|
||||||
|
import SettingsView from '../../views/SettingsView';
|
||||||
|
import LanguageView from '../../views/LanguageView';
|
||||||
|
import ThemeView from '../../views/ThemeView';
|
||||||
|
import DefaultBrowserView from '../../views/DefaultBrowserView';
|
||||||
|
import ScreenLockConfigView from '../../views/ScreenLockConfigView';
|
||||||
|
import AdminPanelView from '../../views/AdminPanelView';
|
||||||
|
import NewMessageView from '../../views/NewMessageView';
|
||||||
|
import CreateChannelView from '../../views/CreateChannelView';
|
||||||
|
|
||||||
|
// InsideStackNavigator
|
||||||
|
import AttachmentView from '../../views/AttachmentView';
|
||||||
|
import ModalBlockView from '../../views/ModalBlockView';
|
||||||
|
import JitsiMeetView from '../../views/JitsiMeetView';
|
||||||
|
import StatusView from '../../views/StatusView';
|
||||||
|
import CreateDiscussionView from '../../views/CreateDiscussionView';
|
||||||
|
|
||||||
|
import { setKeyCommands, deleteKeyCommands } from '../../commands';
|
||||||
|
|
||||||
|
// ChatsStackNavigator
|
||||||
|
const ChatsStack = createStackNavigator();
|
||||||
|
const ChatsStackNavigator = React.memo(() => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
const isFocused = useIsFocused();
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFocused) {
|
||||||
|
setKeyCommands();
|
||||||
|
} else {
|
||||||
|
deleteKeyCommands();
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
deleteKeyCommands();
|
||||||
|
};
|
||||||
|
}, [isFocused]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatsStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='RoomView'
|
||||||
|
component={RoomView}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
</ChatsStack.Navigator>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// DrawerNavigator
|
||||||
|
const Drawer = createDrawerNavigator();
|
||||||
|
const DrawerNavigator = React.memo(() => (
|
||||||
|
<Drawer.Navigator
|
||||||
|
drawerContent={({ navigation, state }) => <RoomsListView navigation={navigation} state={state} />}
|
||||||
|
drawerType='permanent'
|
||||||
|
>
|
||||||
|
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />
|
||||||
|
</Drawer.Navigator>
|
||||||
|
));
|
||||||
|
|
||||||
|
const ModalStack = createStackNavigator();
|
||||||
|
const ModalStackNavigator = React.memo(({ navigation }) => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
return (
|
||||||
|
<ModalContainer navigation={navigation} theme={theme}>
|
||||||
|
<ModalStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='RoomActionsView'
|
||||||
|
component={RoomActionsView}
|
||||||
|
options={props => RoomActionsView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='RoomInfoView'
|
||||||
|
component={RoomInfoView}
|
||||||
|
options={RoomInfoView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='RoomInfoEditView'
|
||||||
|
component={RoomInfoEditView}
|
||||||
|
options={RoomInfoEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='RoomMembersView'
|
||||||
|
component={RoomMembersView}
|
||||||
|
options={RoomMembersView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='SearchMessagesView'
|
||||||
|
component={SearchMessagesView}
|
||||||
|
options={SearchMessagesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='SelectedUsersView'
|
||||||
|
component={SelectedUsersView}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='InviteUsersView'
|
||||||
|
component={InviteUsersView}
|
||||||
|
options={InviteUsersView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='InviteUsersEditView'
|
||||||
|
component={InviteUsersEditView}
|
||||||
|
options={InviteUsersEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='MessagesView'
|
||||||
|
component={MessagesView}
|
||||||
|
options={MessagesView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='AutoTranslateView'
|
||||||
|
component={AutoTranslateView}
|
||||||
|
options={AutoTranslateView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='DirectoryView'
|
||||||
|
component={DirectoryView}
|
||||||
|
options={props => DirectoryView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='NotificationPrefView'
|
||||||
|
component={NotificationPrefView}
|
||||||
|
options={NotificationPrefView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='VisitorNavigationView'
|
||||||
|
component={VisitorNavigationView}
|
||||||
|
options={VisitorNavigationView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ForwardLivechatView'
|
||||||
|
component={ForwardLivechatView}
|
||||||
|
options={ForwardLivechatView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='LivechatEditView'
|
||||||
|
component={LivechatEditView}
|
||||||
|
options={LivechatEditView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='PickerView'
|
||||||
|
component={PickerView}
|
||||||
|
options={PickerView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ThreadMessagesView'
|
||||||
|
component={ThreadMessagesView}
|
||||||
|
options={props => ThreadMessagesView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='MarkdownTableView'
|
||||||
|
component={MarkdownTableView}
|
||||||
|
options={MarkdownTableView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ReadReceiptsView'
|
||||||
|
component={ReadReceiptsView}
|
||||||
|
options={ReadReceiptsView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='SettingsView'
|
||||||
|
component={SettingsView}
|
||||||
|
options={props => SettingsView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='LanguageView'
|
||||||
|
component={LanguageView}
|
||||||
|
options={LanguageView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ThemeView'
|
||||||
|
component={ThemeView}
|
||||||
|
options={ThemeView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='DefaultBrowserView'
|
||||||
|
component={DefaultBrowserView}
|
||||||
|
options={DefaultBrowserView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ScreenLockConfigView'
|
||||||
|
component={ScreenLockConfigView}
|
||||||
|
options={ScreenLockConfigView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='StatusView'
|
||||||
|
component={StatusView}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='ProfileView'
|
||||||
|
component={ProfileView}
|
||||||
|
options={props => ProfileView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='AdminPanelView'
|
||||||
|
component={AdminPanelView}
|
||||||
|
options={props => AdminPanelView.navigationOptions({ ...props, isMasterDetail: true })}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='NewMessageView'
|
||||||
|
component={NewMessageView}
|
||||||
|
options={NewMessageView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='SelectedUsersViewCreateChannel'
|
||||||
|
component={SelectedUsersView}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='CreateChannelView'
|
||||||
|
component={CreateChannelView}
|
||||||
|
options={CreateChannelView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ModalStack.Screen
|
||||||
|
name='CreateDiscussionView'
|
||||||
|
component={CreateDiscussionView}
|
||||||
|
/>
|
||||||
|
</ModalStack.Navigator>
|
||||||
|
</ModalContainer>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ModalStackNavigator.propTypes = {
|
||||||
|
navigation: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
// InsideStackNavigator
|
||||||
|
const InsideStack = createStackNavigator();
|
||||||
|
const InsideStackNavigator = React.memo(() => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
return (
|
||||||
|
<InsideStack.Navigator mode='modal' screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...FadeFromCenterModal }}>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='DrawerNavigator'
|
||||||
|
component={DrawerNavigator}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='ModalStackNavigator'
|
||||||
|
component={ModalStackNavigator}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='AttachmentView'
|
||||||
|
component={AttachmentView}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='ModalBlockView'
|
||||||
|
component={ModalBlockView}
|
||||||
|
options={ModalBlockView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<InsideStack.Screen
|
||||||
|
name='JitsiMeetView'
|
||||||
|
component={JitsiMeetView}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
</InsideStack.Navigator>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const RootInsideStack = React.memo(({ navigation, route }) => (
|
||||||
|
<>
|
||||||
|
<InsideStackNavigator navigation={navigation} />
|
||||||
|
<NotificationBadge navigation={navigation} route={route} />
|
||||||
|
<Toast />
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
RootInsideStack.propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RootInsideStack;
|
|
@ -0,0 +1,101 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { createStackNavigator } from '@react-navigation/stack';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { ThemeContext } from '../theme';
|
||||||
|
import {
|
||||||
|
defaultHeader, themedHeader, StackAnimation, ModalAnimation
|
||||||
|
} from '../utils/navigation';
|
||||||
|
|
||||||
|
// Outside Stack
|
||||||
|
import OnboardingView from '../views/OnboardingView';
|
||||||
|
import NewServerView from '../views/NewServerView';
|
||||||
|
import WorkspaceView from '../views/WorkspaceView';
|
||||||
|
import LoginView from '../views/LoginView';
|
||||||
|
import ForgotPasswordView from '../views/ForgotPasswordView';
|
||||||
|
import RegisterView from '../views/RegisterView';
|
||||||
|
import LegalView from '../views/LegalView';
|
||||||
|
import AuthenticationWebView from '../views/AuthenticationWebView';
|
||||||
|
import { ROOT_OUTSIDE } from '../actions/app';
|
||||||
|
|
||||||
|
// Outside
|
||||||
|
const Outside = createStackNavigator();
|
||||||
|
const _OutsideStack = ({ root }) => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Outside.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation }}>
|
||||||
|
{root === ROOT_OUTSIDE ? (
|
||||||
|
<Outside.Screen
|
||||||
|
name='OnboardingView'
|
||||||
|
component={OnboardingView}
|
||||||
|
options={OnboardingView.navigationOptions}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Outside.Screen
|
||||||
|
name='NewServerView'
|
||||||
|
component={NewServerView}
|
||||||
|
options={NewServerView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<Outside.Screen
|
||||||
|
name='WorkspaceView'
|
||||||
|
component={WorkspaceView}
|
||||||
|
options={WorkspaceView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<Outside.Screen
|
||||||
|
name='LoginView'
|
||||||
|
component={LoginView}
|
||||||
|
options={LoginView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<Outside.Screen
|
||||||
|
name='ForgotPasswordView'
|
||||||
|
component={ForgotPasswordView}
|
||||||
|
options={ForgotPasswordView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<Outside.Screen
|
||||||
|
name='RegisterView'
|
||||||
|
component={RegisterView}
|
||||||
|
options={RegisterView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<Outside.Screen
|
||||||
|
name='LegalView'
|
||||||
|
component={LegalView}
|
||||||
|
options={LegalView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</Outside.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
root: state.app.root
|
||||||
|
});
|
||||||
|
|
||||||
|
_OutsideStack.propTypes = {
|
||||||
|
root: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
const OutsideStack = connect(mapStateToProps)(_OutsideStack);
|
||||||
|
|
||||||
|
// OutsideStackModal
|
||||||
|
const OutsideModal = createStackNavigator();
|
||||||
|
const OutsideStackModal = () => {
|
||||||
|
const { theme } = React.useContext(ThemeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OutsideModal.Navigator mode='modal' screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...ModalAnimation }}>
|
||||||
|
<OutsideModal.Screen
|
||||||
|
name='OutsideStack'
|
||||||
|
component={OutsideStack}
|
||||||
|
options={{ headerShown: false }}
|
||||||
|
/>
|
||||||
|
<OutsideModal.Screen
|
||||||
|
name='AuthenticationWebView'
|
||||||
|
component={AuthenticationWebView}
|
||||||
|
options={AuthenticationWebView.navigationOptions}
|
||||||
|
/>
|
||||||
|
</OutsideModal.Navigator>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OutsideStackModal;
|
222
app/tablet.js
222
app/tablet.js
|
@ -1,222 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { View } from 'react-native';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { NavigationActions, StackActions } from 'react-navigation';
|
|
||||||
import KeyCommands from 'react-native-keycommands';
|
|
||||||
|
|
||||||
import Navigation from './lib/Navigation';
|
|
||||||
import { isSplited } from './utils/deviceInfo';
|
|
||||||
import {
|
|
||||||
App, RoomContainer, ModalContainer, NotificationContainer
|
|
||||||
} from './index';
|
|
||||||
import { MAX_SIDEBAR_WIDTH } from './constants/tablet';
|
|
||||||
import ModalNavigation from './lib/ModalNavigation';
|
|
||||||
import { keyCommands, defaultCommands } from './commands';
|
|
||||||
import { themes } from './constants/colors';
|
|
||||||
|
|
||||||
import sharedStyles from './views/Styles';
|
|
||||||
|
|
||||||
let modalRef;
|
|
||||||
let roomRef;
|
|
||||||
let notificationRef;
|
|
||||||
|
|
||||||
export const initTabletNav = (setState) => {
|
|
||||||
let inCall = false;
|
|
||||||
|
|
||||||
const defaultApp = App.router.getStateForAction;
|
|
||||||
const defaultModal = ModalContainer.router.getStateForAction;
|
|
||||||
const defaultRoom = RoomContainer.router.getStateForAction;
|
|
||||||
const defaultNotification = NotificationContainer.router.getStateForAction;
|
|
||||||
|
|
||||||
NotificationContainer.router.getStateForAction = (action, state) => {
|
|
||||||
if (action.type === NavigationActions.NAVIGATE && isSplited()) {
|
|
||||||
const { routeName, params } = action;
|
|
||||||
if (routeName === 'RoomView') {
|
|
||||||
const resetAction = StackActions.reset({
|
|
||||||
index: 0,
|
|
||||||
actions: [NavigationActions.navigate({ routeName, params })]
|
|
||||||
});
|
|
||||||
roomRef.dispatch(resetAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultNotification(action, state);
|
|
||||||
};
|
|
||||||
|
|
||||||
RoomContainer.router.getStateForAction = (action, state) => {
|
|
||||||
if (action.type === NavigationActions.NAVIGATE && isSplited()) {
|
|
||||||
const { routeName, params } = action;
|
|
||||||
if (routeName === 'RoomActionsView') {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName, params }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (routeName === 'AttachmentView') {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName, params }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action.type === 'Navigation/RESET' && isSplited()) {
|
|
||||||
const { params } = action.actions[action.index];
|
|
||||||
const routes = state.routes[state.index] && state.routes[state.index].params;
|
|
||||||
if (params && params.rid && routes && routes.rid && params.rid === routes.rid) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultRoom(action, state);
|
|
||||||
};
|
|
||||||
|
|
||||||
ModalContainer.router.getStateForAction = (action, state) => {
|
|
||||||
if (action.type === 'Navigation/POP' && isSplited()) {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName: 'AuthLoading' }));
|
|
||||||
setState({ showModal: false });
|
|
||||||
}
|
|
||||||
if (action.type === NavigationActions.NAVIGATE && isSplited()) {
|
|
||||||
const { routeName, params } = action;
|
|
||||||
if (routeName === 'RoomView') {
|
|
||||||
Navigation.navigate(routeName, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultModal(action, state);
|
|
||||||
};
|
|
||||||
|
|
||||||
App.router.getStateForAction = (action, state) => {
|
|
||||||
if (action.type === NavigationActions.NAVIGATE) {
|
|
||||||
const { routeName, params } = action;
|
|
||||||
|
|
||||||
if (routeName === 'InsideStack') {
|
|
||||||
let commands = defaultCommands;
|
|
||||||
let newState = { inside: true };
|
|
||||||
if (isSplited()) {
|
|
||||||
commands = [...commands, ...keyCommands];
|
|
||||||
newState = { ...newState, showModal: false };
|
|
||||||
}
|
|
||||||
KeyCommands.setKeyCommands(commands);
|
|
||||||
setState(newState);
|
|
||||||
}
|
|
||||||
if (isSplited()) {
|
|
||||||
if (routeName === 'ReadReceiptsView') {
|
|
||||||
roomRef.dispatch(NavigationActions.navigate({ routeName, params }));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (routeName === 'OutsideStack') {
|
|
||||||
KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]);
|
|
||||||
setState({ inside: false, showModal: false });
|
|
||||||
}
|
|
||||||
if (routeName === 'JitsiMeetView') {
|
|
||||||
inCall = true;
|
|
||||||
KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]);
|
|
||||||
setState({ inside: false, showModal: false });
|
|
||||||
}
|
|
||||||
if (routeName === 'OnboardingView' || routeName === 'NewServerView') {
|
|
||||||
KeyCommands.deleteKeyCommands([...defaultCommands, ...keyCommands]);
|
|
||||||
setState({ inside: false, showModal: false });
|
|
||||||
}
|
|
||||||
if (routeName === 'ModalBlockView' || routeName === 'StatusView' || routeName === 'CreateDiscussionView') {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName, params }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (routeName === 'RoomView') {
|
|
||||||
const resetAction = StackActions.reset({
|
|
||||||
index: 0,
|
|
||||||
actions: [NavigationActions.navigate({ routeName, params })]
|
|
||||||
});
|
|
||||||
roomRef.dispatch(resetAction);
|
|
||||||
notificationRef.dispatch(resetAction);
|
|
||||||
setState({ showModal: false });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (routeName === 'RoomsListView') {
|
|
||||||
const resetAction = StackActions.reset({
|
|
||||||
index: 0,
|
|
||||||
actions: [NavigationActions.navigate({ routeName: 'RoomView', params: {} })]
|
|
||||||
});
|
|
||||||
roomRef.dispatch(resetAction);
|
|
||||||
notificationRef.dispatch(resetAction);
|
|
||||||
setState({ showModal: false });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (routeName === 'NewMessageView') {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName, params }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (routeName === 'DirectoryView') {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action.type === 'Navigation/TOGGLE_DRAWER' && isSplited()) {
|
|
||||||
modalRef.dispatch(NavigationActions.navigate({ routeName: 'SettingsView' }));
|
|
||||||
setState({ showModal: true });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (action.type === 'Navigation/POP' && inCall) {
|
|
||||||
KeyCommands.setKeyCommands([...defaultCommands, ...keyCommands]);
|
|
||||||
setState({ inside: true, showModal: false });
|
|
||||||
}
|
|
||||||
return defaultApp(action, state);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const Split = ({
|
|
||||||
split, tablet, showModal, closeModal, setModalRef, theme
|
|
||||||
}) => {
|
|
||||||
if (split) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<View style={[sharedStyles.container, sharedStyles.separatorLeft, { borderColor: themes[theme].separatorColor }]}>
|
|
||||||
<RoomContainer ref={ref => roomRef = ref} screenProps={{ split: tablet, theme }} />
|
|
||||||
</View>
|
|
||||||
<ModalContainer showModal={showModal} closeModal={closeModal} ref={setModalRef} screenProps={{ split: tablet, theme }} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tablet = ({
|
|
||||||
children, tablet, theme, inside, showModal, closeModal, onLayout
|
|
||||||
}) => {
|
|
||||||
const setModalRef = (ref) => {
|
|
||||||
modalRef = ref;
|
|
||||||
ModalNavigation.setTopLevelNavigator(modalRef);
|
|
||||||
};
|
|
||||||
|
|
||||||
const split = tablet && inside;
|
|
||||||
return (
|
|
||||||
<View style={sharedStyles.containerSplitView} onLayout={onLayout}>
|
|
||||||
<View style={[sharedStyles.container, split && { maxWidth: MAX_SIDEBAR_WIDTH }]}>
|
|
||||||
{children}
|
|
||||||
</View>
|
|
||||||
<Split split={split} tablet={tablet} theme={theme} showModal={showModal} closeModal={closeModal} setModalRef={setModalRef} />
|
|
||||||
<NotificationContainer ref={ref => notificationRef = ref} screenProps={{ theme }} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Split.propTypes = {
|
|
||||||
split: PropTypes.bool,
|
|
||||||
tablet: PropTypes.bool,
|
|
||||||
showModal: PropTypes.bool,
|
|
||||||
closeModal: PropTypes.func,
|
|
||||||
setModalRef: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
Tablet.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
tablet: PropTypes.bool,
|
|
||||||
inside: PropTypes.bool,
|
|
||||||
showModal: PropTypes.bool,
|
|
||||||
closeModal: PropTypes.func,
|
|
||||||
onLayout: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Tablet;
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
import DeviceInfo from 'react-native-device-info';
|
||||||
|
|
||||||
import { MIN_WIDTH_SPLIT_LAYOUT } from '../constants/tablet';
|
|
||||||
|
|
||||||
export const hasNotch = DeviceInfo.hasNotch();
|
export const hasNotch = DeviceInfo.hasNotch();
|
||||||
export const isIOS = Platform.OS === 'ios';
|
export const isIOS = Platform.OS === 'ios';
|
||||||
export const isAndroid = !isIOS;
|
export const isAndroid = !isIOS;
|
||||||
|
@ -18,10 +16,3 @@ export const supportSystemTheme = () => {
|
||||||
|
|
||||||
// Tablet info
|
// Tablet info
|
||||||
export const isTablet = DeviceInfo.isTablet();
|
export const isTablet = DeviceInfo.isTablet();
|
||||||
|
|
||||||
// We need to use this when app is used on splitview with another app
|
|
||||||
// to handle cases on app view not-larger sufficient to show splited views (room list/room)
|
|
||||||
// https://github.com/RocketChat/Rocket.Chat.ReactNative/pull/1300#discussion_r341405245
|
|
||||||
let _width = null;
|
|
||||||
export const setWidth = width => _width = width;
|
|
||||||
export const isSplited = () => isTablet && _width > MIN_WIDTH_SPLIT_LAYOUT;
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
import Navigation from '../lib/Navigation';
|
import Navigation from '../lib/Navigation';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
|
||||||
const navigate = (item) => {
|
const navigate = ({ item, isMasterDetail, ...props }) => {
|
||||||
Navigation.navigate('RoomView', {
|
let navigationMethod = Navigation.navigate;
|
||||||
|
|
||||||
|
if (isMasterDetail) {
|
||||||
|
navigationMethod = Navigation.replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationMethod('RoomView', {
|
||||||
rid: item.rid,
|
rid: item.rid,
|
||||||
name: RocketChat.getRoomTitle(item),
|
name: RocketChat.getRoomTitle(item),
|
||||||
t: item.t,
|
t: item.t,
|
||||||
|
@ -10,30 +16,32 @@ const navigate = (item) => {
|
||||||
room: item,
|
room: item,
|
||||||
search: item.search,
|
search: item.search,
|
||||||
visitor: item.visitor,
|
visitor: item.visitor,
|
||||||
roomUserId: RocketChat.getUidDirectMessage(item)
|
roomUserId: RocketChat.getUidDirectMessage(item),
|
||||||
|
...props
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const goRoom = async(item = {}) => {
|
export const goRoom = async({ item = {}, isMasterDetail = false, ...props }) => {
|
||||||
if (!item.search) {
|
if (item.t === 'd' && item.search) {
|
||||||
return navigate(item);
|
|
||||||
}
|
|
||||||
if (item.t === 'd') {
|
|
||||||
// if user is using the search we need first to join/create room
|
// if user is using the search we need first to join/create room
|
||||||
try {
|
try {
|
||||||
const { username } = item;
|
const { username } = item;
|
||||||
const result = await RocketChat.createDirectMessage(username);
|
const result = await RocketChat.createDirectMessage(username);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return navigate({
|
return navigate({
|
||||||
|
item: {
|
||||||
rid: result.room._id,
|
rid: result.room._id,
|
||||||
name: username,
|
name: username,
|
||||||
t: 'd'
|
t: 'd'
|
||||||
|
},
|
||||||
|
isMasterDetail,
|
||||||
|
...props
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return navigate(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return navigate({ item, isMasterDetail, ...props });
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,11 @@ export const logServerVersion = (serverVersion) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setCurrentScreen = (currentScreen) => {
|
||||||
|
analytics().setCurrentScreen(currentScreen);
|
||||||
|
leaveBreadcrumb(currentScreen, { type: 'navigation' });
|
||||||
|
};
|
||||||
|
|
||||||
export default (e) => {
|
export default (e) => {
|
||||||
if (e instanceof Error && e.message !== 'Aborted' && !__DEV__) {
|
if (e instanceof Error && e.message !== 'Aborted' && !__DEV__) {
|
||||||
bugsnag.notify(e, (report) => {
|
bugsnag.notify(e, (report) => {
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
import { StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import { analytics, leaveBreadcrumb } from './log';
|
|
||||||
import { themes } from '../constants/colors';
|
|
||||||
|
|
||||||
export const defaultHeader = {
|
|
||||||
headerBackTitle: null
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cardStyle = {
|
|
||||||
backgroundColor: 'rgba(0,0,0,0)'
|
|
||||||
};
|
|
||||||
|
|
||||||
const borderBottom = theme => ({
|
|
||||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
||||||
borderBottomColor: themes[theme].headerBorder,
|
|
||||||
elevation: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
export const themedHeader = theme => ({
|
|
||||||
headerStyle: {
|
|
||||||
...borderBottom(theme),
|
|
||||||
backgroundColor: themes[theme].headerBackground
|
|
||||||
},
|
|
||||||
headerTintColor: themes[theme].headerTintColor,
|
|
||||||
headerTitleStyle: { color: themes[theme].headerTitleColor }
|
|
||||||
});
|
|
||||||
|
|
||||||
// gets the current screen from navigation state
|
|
||||||
export const getActiveRouteName = (navigationState) => {
|
|
||||||
if (!navigationState) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const route = navigationState.routes[navigationState.index];
|
|
||||||
// dive into nested navigators
|
|
||||||
if (route.routes) {
|
|
||||||
return getActiveRouteName(route);
|
|
||||||
}
|
|
||||||
return route.routeName;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onNavigationStateChange = (prevState, currentState) => {
|
|
||||||
const currentScreen = getActiveRouteName(currentState);
|
|
||||||
const prevScreen = getActiveRouteName(prevState);
|
|
||||||
|
|
||||||
if (prevScreen !== currentScreen) {
|
|
||||||
analytics().setCurrentScreen(currentScreen);
|
|
||||||
leaveBreadcrumb(currentScreen, { type: 'navigation' });
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import { Easing, Animated } from 'react-native';
|
||||||
|
import { TransitionPresets, HeaderStyleInterpolators } from '@react-navigation/stack';
|
||||||
|
|
||||||
|
import { isAndroid } from '../deviceInfo';
|
||||||
|
import conditional from './conditional';
|
||||||
|
|
||||||
|
const { multiply } = Animated;
|
||||||
|
|
||||||
|
const forFadeFromCenter = ({
|
||||||
|
current,
|
||||||
|
closing
|
||||||
|
}) => {
|
||||||
|
const opacity = conditional(
|
||||||
|
closing,
|
||||||
|
current.progress,
|
||||||
|
current.progress.interpolate({
|
||||||
|
inputRange: [0, 0.5, 0.9, 1],
|
||||||
|
outputRange: [0, 0.25, 0.7, 1]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cardStyle: {
|
||||||
|
opacity
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const FadeIn = {
|
||||||
|
animation: 'timing',
|
||||||
|
config: {
|
||||||
|
duration: 250,
|
||||||
|
easing: Easing.out(Easing.poly(5))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FadeOut = {
|
||||||
|
animation: 'timing',
|
||||||
|
config: {
|
||||||
|
duration: 150,
|
||||||
|
easing: Easing.in(Easing.poly(5))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FadeFromCenterModal = {
|
||||||
|
gestureDirection: 'vertical',
|
||||||
|
transitionSpec: {
|
||||||
|
open: FadeIn,
|
||||||
|
close: FadeOut
|
||||||
|
},
|
||||||
|
cardStyleInterpolator: forFadeFromCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
const forStackAndroid = ({
|
||||||
|
current,
|
||||||
|
inverted,
|
||||||
|
layouts: { screen },
|
||||||
|
closing
|
||||||
|
}) => {
|
||||||
|
const translateX = multiply(
|
||||||
|
current.progress.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [screen.width, 0]
|
||||||
|
}),
|
||||||
|
inverted
|
||||||
|
);
|
||||||
|
|
||||||
|
const opacity = conditional(
|
||||||
|
closing,
|
||||||
|
current.progress,
|
||||||
|
current.progress.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [0, 1]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cardStyle: {
|
||||||
|
opacity,
|
||||||
|
transform: [{ translateX }]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const StackAndroid = {
|
||||||
|
gestureDirection: 'horizontal',
|
||||||
|
transitionSpec: {
|
||||||
|
open: FadeIn,
|
||||||
|
close: FadeOut
|
||||||
|
},
|
||||||
|
cardStyleInterpolator: forStackAndroid,
|
||||||
|
headerStyleInterpolator: HeaderStyleInterpolators.forFade
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StackAnimation = isAndroid ? StackAndroid : TransitionPresets.SlideFromRightIOS;
|
||||||
|
export const ModalAnimation = TransitionPresets.ModalTransition;
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Taken from https://github.com/react-navigation/react-navigation/blob/master/packages/stack/src/utils/conditional.tsx
|
||||||
|
import { Animated } from 'react-native';
|
||||||
|
|
||||||
|
const { add, multiply } = Animated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use an Animated Node based on a condition. Similar to Reanimated's `cond`.
|
||||||
|
*
|
||||||
|
* @param condition Animated Node representing the condition, must be 0 or 1, 1 means `true`, 0 means `false`
|
||||||
|
* @param main Animated Node to use if the condition is `true`
|
||||||
|
* @param fallback Animated Node to use if the condition is `false`
|
||||||
|
*/
|
||||||
|
export default function conditional(condition, main, fallback) {
|
||||||
|
// To implement this behavior, we multiply the main node with the condition.
|
||||||
|
// So if condition is 0, result will be 0, and if condition is 1, result will be main node.
|
||||||
|
// Then we multiple reverse of the condition (0 if condition is 1) with the fallback.
|
||||||
|
// So if condition is 0, result will be fallback node, and if condition is 1, result will be 0,
|
||||||
|
// This way, one of them will always be 0, and other one will be the value we need.
|
||||||
|
// In the end we add them both together, 0 + value we need = value we need
|
||||||
|
return add(
|
||||||
|
multiply(condition, main),
|
||||||
|
multiply(
|
||||||
|
condition.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [1, 0]
|
||||||
|
}),
|
||||||
|
fallback
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { DefaultTheme, DarkTheme } from '@react-navigation/native';
|
||||||
|
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
|
export * from './animations';
|
||||||
|
|
||||||
|
export const defaultHeader = {
|
||||||
|
headerBackTitleVisible: false,
|
||||||
|
cardOverlayEnabled: true,
|
||||||
|
cardStyle: { backgroundColor: 'transparent' }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const cardStyle = {
|
||||||
|
backgroundColor: 'rgba(0,0,0,0)'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const borderBottom = theme => ({
|
||||||
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||||
|
borderBottomColor: themes[theme].headerBorder,
|
||||||
|
elevation: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
export const themedHeader = theme => ({
|
||||||
|
headerStyle: {
|
||||||
|
...borderBottom(theme),
|
||||||
|
backgroundColor: themes[theme].headerBackground
|
||||||
|
},
|
||||||
|
headerTintColor: themes[theme].headerTintColor,
|
||||||
|
headerTitleStyle: { color: themes[theme].headerTitleColor }
|
||||||
|
});
|
||||||
|
|
||||||
|
export const navigationTheme = (theme) => {
|
||||||
|
const defaultNavTheme = theme === 'light' ? DefaultTheme : DarkTheme;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...defaultNavTheme,
|
||||||
|
colors: {
|
||||||
|
...defaultNavTheme.colors,
|
||||||
|
background: themes[theme].backgroundColor,
|
||||||
|
border: themes[theme].borderColor
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Gets the current screen from navigation state
|
||||||
|
export const getActiveRoute = (state) => {
|
||||||
|
const route = state.routes[state.index];
|
||||||
|
|
||||||
|
if (route.state) {
|
||||||
|
// Dive into nested navigators
|
||||||
|
return getActiveRoute(route.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return route;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getActiveRouteName = state => getActiveRoute(state).name;
|
|
@ -1,22 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { WebView } from 'react-native-webview';
|
import { WebView } from 'react-native-webview';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { DrawerButton } from '../../containers/HeaderButton';
|
import { DrawerButton } from '../../containers/HeaderButton';
|
||||||
import styles from '../Styles';
|
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
class AdminPanelView extends React.Component {
|
class AdminPanelView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => ({
|
static navigationOptions = ({ navigation, isMasterDetail }) => ({
|
||||||
...themedHeader(screenProps.theme),
|
headerLeft: isMasterDetail ? undefined : () => <DrawerButton navigation={navigation} />,
|
||||||
headerLeft: <DrawerButton navigation={navigation} />,
|
|
||||||
title: I18n.t('Admin_Panel')
|
title: I18n.t('Admin_Panel')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -32,7 +28,7 @@ class AdminPanelView extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} testID='terms-view'>
|
<SafeAreaView theme={theme}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<WebView
|
<WebView
|
||||||
source={{ uri: `${ baseUrl }/admin/info?layout=embedded` }}
|
source={{ uri: `${ baseUrl }/admin/info?layout=embedded` }}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import EventEmitter from '../utils/events';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import ImageViewer from '../presentation/ImageViewer';
|
import ImageViewer from '../presentation/ImageViewer';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { formatAttachmentUrl } from '../lib/utils';
|
import { formatAttachmentUrl } from '../lib/utils';
|
||||||
import RCActivityIndicator from '../containers/ActivityIndicator';
|
import RCActivityIndicator from '../containers/ActivityIndicator';
|
||||||
|
@ -28,26 +27,9 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class AttachmentView extends React.Component {
|
class AttachmentView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
|
||||||
const { theme } = screenProps;
|
|
||||||
const attachment = navigation.getParam('attachment');
|
|
||||||
const from = navigation.getParam('from');
|
|
||||||
const handleSave = navigation.getParam('handleSave', () => {});
|
|
||||||
const { title } = attachment;
|
|
||||||
const options = {
|
|
||||||
title: decodeURI(title),
|
|
||||||
...themedHeader(theme),
|
|
||||||
headerRight: <SaveButton testID='save-image' onPress={handleSave} />
|
|
||||||
};
|
|
||||||
if (from !== 'MessagesView') {
|
|
||||||
options.gesturesEnabled = false;
|
|
||||||
options.headerLeft = <CloseModalButton testID='close-attachment-view' navigation={navigation} />;
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
|
@ -58,15 +40,14 @@ class AttachmentView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const attachment = props.navigation.getParam('attachment');
|
const attachment = props.route.params?.attachment;
|
||||||
this.state = { attachment, loading: true };
|
this.state = { attachment, loading: true };
|
||||||
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.setParams({ handleSave: this.handleSave });
|
this.unsubscribeBlur = navigation.addListener('blur', () => {
|
||||||
|
|
||||||
this.willBlurListener = navigation.addListener('willBlur', () => {
|
|
||||||
if (this.videoRef && this.videoRef.stopAsync) {
|
if (this.videoRef && this.videoRef.stopAsync) {
|
||||||
this.videoRef.stopAsync();
|
this.videoRef.stopAsync();
|
||||||
}
|
}
|
||||||
|
@ -74,11 +55,23 @@ class AttachmentView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this.willBlurListener && this.willBlurListener.remove) {
|
if (this.unsubscribeBlur) {
|
||||||
this.willBlurListener.remove();
|
this.unsubscribeBlur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHeader = () => {
|
||||||
|
const { route, navigation } = this.props;
|
||||||
|
const attachment = route.params?.attachment;
|
||||||
|
const { title } = attachment;
|
||||||
|
const options = {
|
||||||
|
headerLeft: () => <CloseModalButton testID='close-attachment-view' navigation={navigation} />,
|
||||||
|
title: decodeURI(title),
|
||||||
|
headerRight: () => <SaveButton testID='save-image' onPress={this.handleSave} />
|
||||||
|
};
|
||||||
|
navigation.setOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
getVideoRef = ref => this.videoRef = ref;
|
getVideoRef = ref => this.videoRef = ref;
|
||||||
|
|
||||||
handleSave = async() => {
|
handleSave = async() => {
|
||||||
|
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
||||||
import {
|
import {
|
||||||
View, Text, StyleSheet, ActivityIndicator
|
View, Text, StyleSheet, ActivityIndicator
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
|
@ -24,9 +26,7 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default React.memo(withTheme(({ theme, navigation }) => {
|
const AuthLoadingView = React.memo(({ theme, text }) => (
|
||||||
const text = navigation.getParam('text');
|
|
||||||
return (
|
|
||||||
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
|
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
{text && (
|
{text && (
|
||||||
|
@ -36,5 +36,15 @@ export default React.memo(withTheme(({ theme, navigation }) => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
));
|
||||||
}));
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
text: state.app.text
|
||||||
|
});
|
||||||
|
|
||||||
|
AuthLoadingView.propTypes = {
|
||||||
|
theme: PropTypes.string,
|
||||||
|
text: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(withTheme(AuthLoadingView));
|
||||||
|
|
|
@ -6,29 +6,20 @@ import parse from 'url-parse';
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
import { CloseModalButton } from '../containers/HeaderButton';
|
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import ActivityIndicator from '../containers/ActivityIndicator';
|
import ActivityIndicator from '../containers/ActivityIndicator';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
|
import { CloseModalButton } from '../containers/HeaderButton';
|
||||||
|
|
||||||
const userAgent = isIOS
|
const userAgent = isIOS
|
||||||
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1'
|
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1'
|
||||||
: 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36';
|
: 'Mozilla/5.0 (Linux; Android 6.0.1; SM-G920V Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36';
|
||||||
|
|
||||||
class AuthenticationWebView extends React.PureComponent {
|
class AuthenticationWebView extends React.PureComponent {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
|
||||||
const authType = navigation.getParam('authType', 'oauth');
|
|
||||||
return {
|
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
headerLeft: <CloseModalButton navigation={navigation} />,
|
|
||||||
title: authType === 'saml' || authType === 'cas' ? 'SSO' : 'OAuth'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
}
|
}
|
||||||
|
@ -39,7 +30,6 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
logging: false,
|
logging: false,
|
||||||
loading: false
|
loading: false
|
||||||
};
|
};
|
||||||
this.authType = props.navigation.getParam('authType', 'oauth');
|
|
||||||
this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
|
this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,14 +66,15 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
|
|
||||||
onNavigationStateChange = (webViewState) => {
|
onNavigationStateChange = (webViewState) => {
|
||||||
const url = decodeURIComponent(webViewState.url);
|
const url = decodeURIComponent(webViewState.url);
|
||||||
if (this.authType === 'saml' || this.authType === 'cas') {
|
const { route } = this.props;
|
||||||
const { navigation } = this.props;
|
const { authType } = route.params;
|
||||||
const ssoToken = navigation.getParam('ssoToken');
|
if (authType === 'saml' || authType === 'cas') {
|
||||||
|
const { ssoToken } = route.params;
|
||||||
const parsedUrl = parse(url, true);
|
const parsedUrl = parse(url, true);
|
||||||
// ticket -> cas / validate & saml_idp_credentialToken -> saml
|
// ticket -> cas / validate & saml_idp_credentialToken -> saml
|
||||||
if (parsedUrl.pathname?.includes('validate') || parsedUrl.query?.ticket || parsedUrl.query?.saml_idp_credentialToken) {
|
if (parsedUrl.pathname?.includes('validate') || parsedUrl.query?.ticket || parsedUrl.query?.saml_idp_credentialToken) {
|
||||||
let payload;
|
let payload;
|
||||||
if (this.authType === 'saml') {
|
if (authType === 'saml') {
|
||||||
const token = parsedUrl.query?.saml_idp_credentialToken || ssoToken;
|
const token = parsedUrl.query?.saml_idp_credentialToken || ssoToken;
|
||||||
const credentialToken = { credentialToken: token };
|
const credentialToken = { credentialToken: token };
|
||||||
payload = { ...credentialToken, saml: true };
|
payload = { ...credentialToken, saml: true };
|
||||||
|
@ -94,7 +85,7 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.authType === 'oauth') {
|
if (authType === 'oauth') {
|
||||||
if (this.redirectRegex.test(url)) {
|
if (this.redirectRegex.test(url)) {
|
||||||
const parts = url.split('#');
|
const parts = url.split('#');
|
||||||
const credentials = JSON.parse(parts[1]);
|
const credentials = JSON.parse(parts[1]);
|
||||||
|
@ -105,13 +96,13 @@ class AuthenticationWebView extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading } = this.state;
|
const { loading } = this.state;
|
||||||
const { navigation, theme } = this.props;
|
const { route, theme } = this.props;
|
||||||
const uri = navigation.getParam('url');
|
const { url } = route.params;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<WebView
|
<WebView
|
||||||
source={{ uri }}
|
source={{ uri: url }}
|
||||||
userAgent={userAgent}
|
userAgent={userAgent}
|
||||||
onNavigationStateChange={this.onNavigationStateChange}
|
onNavigationStateChange={this.onNavigationStateChange}
|
||||||
onLoadStart={() => {
|
onLoadStart={() => {
|
||||||
|
@ -131,4 +122,12 @@ const mapStateToProps = state => ({
|
||||||
server: state.server.server
|
server: state.server.server
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AuthenticationWebView.navigationOptions = ({ route, navigation }) => {
|
||||||
|
const { authType } = route.params;
|
||||||
|
return {
|
||||||
|
headerLeft: () => <CloseModalButton navigation={navigation} />,
|
||||||
|
title: authType === 'saml' || authType === 'cas' ? 'SSO' : 'OAuth'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
FlatList, Switch, View, StyleSheet
|
FlatList, Switch, View, StyleSheet, ScrollView
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView, ScrollView } from 'react-navigation';
|
|
||||||
|
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -15,7 +14,7 @@ import Separator from '../../containers/Separator';
|
||||||
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
contentContainerStyle: {
|
contentContainerStyle: {
|
||||||
|
@ -49,20 +48,19 @@ SectionSeparator.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoTranslateView extends React.Component {
|
class AutoTranslateView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Auto_Translate'),
|
title: I18n.t('Auto_Translate')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
const room = props.navigation.getParam('room');
|
const room = props.route.params?.room;
|
||||||
|
|
||||||
if (room && room.observe) {
|
if (room && room.observe) {
|
||||||
this.roomObservable = room.observe();
|
this.roomObservable = room.observe();
|
||||||
|
@ -163,11 +161,7 @@ class AutoTranslateView extends React.Component {
|
||||||
const { languages } = this.state;
|
const { languages } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='auto-translate-view' theme={theme}>
|
||||||
style={[sharedStyles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID='auto-translate-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
View, Text, Switch, ScrollView, StyleSheet, FlatList
|
View, Text, Switch, ScrollView, StyleSheet, FlatList
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
|
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../presentation/TextInput';
|
||||||
|
@ -20,9 +19,9 @@ import { CustomHeaderButtons, Item } from '../containers/HeaderButton';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import { SWITCH_TRACK_COLOR, themes } from '../constants/colors';
|
import { SWITCH_TRACK_COLOR, themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import { Review } from '../utils/review';
|
import { Review } from '../utils/review';
|
||||||
import { getUserSelector } from '../selectors/login';
|
import { getUserSelector } from '../selectors/login';
|
||||||
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -73,22 +72,8 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class CreateChannelView extends React.Component {
|
class CreateChannelView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = {
|
||||||
const submit = navigation.getParam('submit', () => {});
|
title: I18n.t('Create_Channel')
|
||||||
const showSubmit = navigation.getParam('showSubmit');
|
|
||||||
return {
|
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
title: I18n.t('Create_Channel'),
|
|
||||||
headerRight: (
|
|
||||||
showSubmit
|
|
||||||
? (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item title={I18n.t('Create')} onPress={submit} testID='create-channel-submit' />
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -114,11 +99,6 @@ class CreateChannelView extends React.Component {
|
||||||
broadcast: false
|
broadcast: false
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { navigation } = this.props;
|
|
||||||
navigation.setParams({ submit: this.submit });
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const {
|
const {
|
||||||
channelName, type, readOnly, broadcast
|
channelName, type, readOnly, broadcast
|
||||||
|
@ -148,9 +128,19 @@ class CreateChannelView extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeText = (channelName) => {
|
toggleRightButton = (channelName) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.setParams({ showSubmit: channelName.trim().length > 0 });
|
navigation.setOptions({
|
||||||
|
headerRight: () => channelName.trim().length > 0 && (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item title={I18n.t('Create')} onPress={this.submit} testID='create-channel-submit' />
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeText = (channelName) => {
|
||||||
|
this.toggleRightButton(channelName);
|
||||||
this.setState({ channelName });
|
this.setState({ channelName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +284,7 @@ class CreateChannelView extends React.Component {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<SafeAreaView testID='create-channel-view' style={styles.container} forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='create-channel-view' theme={theme}>
|
||||||
<ScrollView {...scrollPersistTaps}>
|
<ScrollView {...scrollPersistTaps}>
|
||||||
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
|
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ScrollView, Text } from 'react-native';
|
import { ScrollView, Text } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
|
|
||||||
import Loading from '../../containers/Loading';
|
import Loading from '../../containers/Loading';
|
||||||
|
@ -13,7 +12,6 @@ import { CustomHeaderButtons, Item, CloseModalButton } from '../../containers/He
|
||||||
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 { themedHeader } from '../../utils/navigation';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import TextInput from '../../containers/TextInput';
|
import TextInput from '../../containers/TextInput';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -25,29 +23,13 @@ import SelectChannel from './SelectChannel';
|
||||||
import SelectUsers from './SelectUsers';
|
import SelectUsers from './SelectUsers';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
import { goRoom } from '../../utils/goRoom';
|
||||||
|
|
||||||
class CreateChannelView extends React.Component {
|
class CreateChannelView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
|
||||||
const submit = navigation.getParam('submit', () => {});
|
|
||||||
const showSubmit = navigation.getParam('showSubmit', navigation.getParam('message'));
|
|
||||||
return {
|
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
title: I18n.t('Create_Discussion'),
|
|
||||||
headerRight: (
|
|
||||||
showSubmit
|
|
||||||
? (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item title={I18n.t('Create')} onPress={submit} testID='create-discussion-submit' />
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
),
|
|
||||||
headerLeft: <CloseModalButton navigation={navigation} />
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
propTypes = {
|
propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
create: PropTypes.func,
|
create: PropTypes.func,
|
||||||
|
@ -55,31 +37,32 @@ class CreateChannelView extends React.Component {
|
||||||
result: PropTypes.object,
|
result: PropTypes.object,
|
||||||
failure: PropTypes.bool,
|
failure: PropTypes.bool,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const { navigation } = props;
|
const { route } = props;
|
||||||
navigation.setParams({ submit: this.submit });
|
this.channel = route.params?.channel;
|
||||||
this.channel = navigation.getParam('channel');
|
const message = route.params?.message ?? {};
|
||||||
const message = navigation.getParam('message', {});
|
|
||||||
this.state = {
|
this.state = {
|
||||||
channel: this.channel,
|
channel: this.channel,
|
||||||
message,
|
message,
|
||||||
name: message.msg || '',
|
name: message?.msg || '',
|
||||||
users: [],
|
users: [],
|
||||||
reply: ''
|
reply: ''
|
||||||
};
|
};
|
||||||
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
const {
|
const {
|
||||||
loading, failure, error, result, navigation
|
loading, failure, error, result, isMasterDetail
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!isEqual(this.state, prevState)) {
|
if (!isEqual(this.state, prevState)) {
|
||||||
navigation.setParams({ showSubmit: this.valid() });
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loading && loading !== prevProps.loading) {
|
if (!loading && loading !== prevProps.loading) {
|
||||||
|
@ -89,17 +72,38 @@ class CreateChannelView extends React.Component {
|
||||||
showErrorAlert(msg);
|
showErrorAlert(msg);
|
||||||
} else {
|
} else {
|
||||||
const { rid, t, prid } = result;
|
const { rid, t, prid } = result;
|
||||||
if (this.channel) {
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
} else {
|
||||||
Navigation.navigate('RoomsListView');
|
Navigation.navigate('RoomsListView');
|
||||||
}
|
}
|
||||||
Navigation.navigate('RoomView', {
|
const item = {
|
||||||
rid, name: RocketChat.getRoomTitle(result), t, prid
|
rid, name: RocketChat.getRoomTitle(result), t, prid
|
||||||
});
|
};
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHeader = () => {
|
||||||
|
const { navigation, route } = this.props;
|
||||||
|
const showCloseModal = route.params?.showCloseModal;
|
||||||
|
navigation.setOptions({
|
||||||
|
title: I18n.t('Create_Discussion'),
|
||||||
|
headerRight: (
|
||||||
|
this.valid()
|
||||||
|
? () => (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item title={I18n.t('Create')} onPress={this.submit} testID='create-discussion-submit' />
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
),
|
||||||
|
headerLeft: showCloseModal ? () => <CloseModalButton navigation={navigation} /> : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
const {
|
const {
|
||||||
name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users
|
name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users
|
||||||
|
@ -137,7 +141,7 @@ class CreateChannelView extends React.Component {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<SafeAreaView testID='create-discussion-view' style={styles.container} forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='create-discussion-view' style={styles.container} theme={theme}>
|
||||||
<ScrollView {...scrollPersistTaps}>
|
<ScrollView {...scrollPersistTaps}>
|
||||||
<Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Discussion_Desc')}</Text>
|
<Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Discussion_Desc')}</Text>
|
||||||
<SelectChannel
|
<SelectChannel
|
||||||
|
@ -187,7 +191,8 @@ const mapStateToProps = state => ({
|
||||||
error: state.createDiscussion.error,
|
error: state.createDiscussion.error,
|
||||||
failure: state.createDiscussion.failure,
|
failure: state.createDiscussion.failure,
|
||||||
loading: state.createDiscussion.isFetching,
|
loading: state.createDiscussion.isFetching,
|
||||||
result: state.createDiscussion.result
|
result: state.createDiscussion.result,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -3,11 +3,9 @@ import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
StyleSheet, FlatList, View, Text, Linking
|
StyleSheet, FlatList, View, Text, Linking
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import RNUserDefaults from 'rn-user-defaults';
|
import RNUserDefaults from 'rn-user-defaults';
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
|
@ -17,6 +15,7 @@ import ListItem from '../containers/ListItem';
|
||||||
import { CustomIcon } from '../lib/Icons';
|
import { CustomIcon } from '../lib/Icons';
|
||||||
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
|
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
|
||||||
const DEFAULT_BROWSERS = [
|
const DEFAULT_BROWSERS = [
|
||||||
{
|
{
|
||||||
|
@ -60,10 +59,9 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class DefaultBrowserView extends React.Component {
|
class DefaultBrowserView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Default_browser'),
|
title: I18n.t('Default_browser')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
@ -164,11 +162,7 @@ class DefaultBrowserView extends React.Component {
|
||||||
const { supported } = this.state;
|
const { supported } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='default-browser-view' theme={theme}>
|
||||||
style={[sharedStyles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID='default-browser-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={DEFAULT_BROWSERS.concat(supported)}
|
data={DEFAULT_BROWSERS.concat(supported)}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
View, FlatList, Text
|
View, FlatList, Text
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -22,17 +21,17 @@ import Options from './Options';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
import { goRoom } from '../../utils/goRoom';
|
||||||
|
|
||||||
class DirectoryView extends React.Component {
|
class DirectoryView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ navigation, isMasterDetail }) => {
|
||||||
const options = {
|
const options = {
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
title: I18n.t('Directory')
|
title: I18n.t('Directory')
|
||||||
};
|
};
|
||||||
if (screenProps.split) {
|
if (isMasterDetail) {
|
||||||
options.headerLeft = <CloseModalButton navigation={navigation} testID='directory-view-close' />;
|
options.headerLeft = () => <CloseModalButton navigation={navigation} testID='directory-view-close' />;
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,8 @@ class DirectoryView extends React.Component {
|
||||||
token: PropTypes.string
|
token: PropTypes.string
|
||||||
}),
|
}),
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
directoryDefaultView: PropTypes.string
|
directoryDefaultView: PropTypes.string,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -125,14 +125,14 @@ class DirectoryView extends React.Component {
|
||||||
this.setState(({ showOptionsDropdown }) => ({ showOptionsDropdown: !showOptionsDropdown }));
|
this.setState(({ showOptionsDropdown }) => ({ showOptionsDropdown: !showOptionsDropdown }));
|
||||||
}
|
}
|
||||||
|
|
||||||
goRoom = async({
|
goRoom = (item) => {
|
||||||
rid, name, t, search
|
const { navigation, isMasterDetail } = this.props;
|
||||||
}) => {
|
if (isMasterDetail) {
|
||||||
const { navigation } = this.props;
|
navigation.navigate('DrawerNavigator');
|
||||||
await navigation.navigate('RoomsListView');
|
} else {
|
||||||
navigation.navigate('RoomView', {
|
navigation.navigate('RoomsListView');
|
||||||
rid, name, t, search
|
}
|
||||||
});
|
goRoom({ item, isMasterDetail });
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressItem = async(item) => {
|
onPressItem = async(item) => {
|
||||||
|
@ -230,7 +230,11 @@ class DirectoryView extends React.Component {
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { isFederationEnabled, theme } = this.props;
|
const { isFederationEnabled, theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.safeAreaView, { backgroundColor: themes[theme].backgroundColor }]} testID='directory-view' forceInset={{ vertical: 'never' }}>
|
<SafeAreaView
|
||||||
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
|
testID='directory-view'
|
||||||
|
theme={theme}
|
||||||
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={data}
|
data={data}
|
||||||
|
@ -267,7 +271,8 @@ const mapStateToProps = state => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
isFederationEnabled: state.settings.FEDERATION_Enabled,
|
isFederationEnabled: state.settings.FEDERATION_Enabled,
|
||||||
directoryDefaultView: state.settings.Accounts_Directory_DefaultView
|
directoryDefaultView: state.settings.Accounts_Directory_DefaultView,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(DirectoryView));
|
export default connect(mapStateToProps)(withTheme(DirectoryView));
|
||||||
|
|
|
@ -3,9 +3,6 @@ import { StyleSheet } from 'react-native';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
safeAreaView: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
list: {
|
list: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,17 +11,12 @@ import I18n from '../i18n';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
|
|
||||||
class ForgotPasswordView extends React.Component {
|
class ForgotPasswordView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ route }) => ({
|
||||||
const title = navigation.getParam('title', 'Rocket.Chat');
|
title: route.params?.title ?? 'Rocket.Chat'
|
||||||
return {
|
})
|
||||||
title,
|
|
||||||
...themedHeader(screenProps.theme)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
|
|
@ -18,14 +18,16 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const ForwardLivechatView = ({ forwardRoom, navigation, theme }) => {
|
const ForwardLivechatView = ({
|
||||||
|
forwardRoom, navigation, route, theme
|
||||||
|
}) => {
|
||||||
const [departments, setDepartments] = useState([]);
|
const [departments, setDepartments] = useState([]);
|
||||||
const [departmentId, setDepartment] = useState();
|
const [departmentId, setDepartment] = useState();
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [userId, setUser] = useState();
|
const [userId, setUser] = useState();
|
||||||
const [room, setRoom] = useState();
|
const [room, setRoom] = useState();
|
||||||
|
|
||||||
const rid = navigation.getParam('rid');
|
const rid = route.params?.rid;
|
||||||
|
|
||||||
const getDepartments = async() => {
|
const getDepartments = async() => {
|
||||||
try {
|
try {
|
||||||
|
@ -137,6 +139,7 @@ const ForwardLivechatView = ({ forwardRoom, navigation, theme }) => {
|
||||||
ForwardLivechatView.propTypes = {
|
ForwardLivechatView.propTypes = {
|
||||||
forwardRoom: PropTypes.func,
|
forwardRoom: PropTypes.func,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
ForwardLivechatView.navigationOptions = {
|
ForwardLivechatView.navigationOptions = {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ScrollView, View } from 'react-native';
|
import { ScrollView, View } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import RNPickerSelect from 'react-native-picker-select';
|
import RNPickerSelect from 'react-native-picker-select';
|
||||||
|
|
||||||
|
@ -17,8 +16,8 @@ import I18n from '../../i18n';
|
||||||
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 { themedHeader } from '../../utils/navigation';
|
|
||||||
import Separator from '../../containers/Separator';
|
import Separator from '../../containers/Separator';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const OPTIONS = {
|
const OPTIONS = {
|
||||||
days: [{
|
days: [{
|
||||||
|
@ -60,13 +59,13 @@ const OPTIONS = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class InviteUsersView extends React.Component {
|
class InviteUsersView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Invite_users'),
|
title: I18n.t('Invite_users')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
timeDateFormat: PropTypes.string,
|
timeDateFormat: PropTypes.string,
|
||||||
createInviteLink: PropTypes.func,
|
createInviteLink: PropTypes.func,
|
||||||
|
@ -75,7 +74,7 @@ class InviteUsersView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChangePicker = (key, value) => {
|
onValueChangePicker = (key, value) => {
|
||||||
|
@ -111,7 +110,7 @@ class InviteUsersView extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} forceInset={{ vertical: 'never' }}>
|
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} theme={theme}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||||
|
|
|
@ -3,9 +3,6 @@ import { StyleSheet } from 'react-native';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
innerContainer: {
|
innerContainer: {
|
||||||
paddingHorizontal: 20
|
paddingHorizontal: 20
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, Share, ScrollView } from 'react-native';
|
import { View, Share, ScrollView } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
@ -18,16 +17,16 @@ import I18n from '../../i18n';
|
||||||
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 { themedHeader } from '../../utils/navigation';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
class InviteUsersView extends React.Component {
|
class InviteUsersView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Invite_users'),
|
title: I18n.t('Invite_users')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
timeDateFormat: PropTypes.string,
|
timeDateFormat: PropTypes.string,
|
||||||
invite: PropTypes.object,
|
invite: PropTypes.object,
|
||||||
|
@ -37,7 +36,7 @@ class InviteUsersView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -100,7 +99,7 @@ class InviteUsersView extends React.Component {
|
||||||
theme, invite
|
theme, invite
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} forceInset={{ vertical: 'never' }}>
|
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} theme={theme}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
innerContainer: {
|
innerContainer: {
|
||||||
padding: 20,
|
padding: 20,
|
||||||
paddingBottom: 0
|
paddingBottom: 0
|
||||||
|
|
|
@ -16,6 +16,7 @@ const formatUrl = (url, baseUrl, uriSize, avatarAuthURLFragment) => (
|
||||||
class JitsiMeetView extends React.Component {
|
class JitsiMeetView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
|
@ -27,14 +28,14 @@ class JitsiMeetView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
this.onConferenceTerminated = this.onConferenceTerminated.bind(this);
|
this.onConferenceTerminated = this.onConferenceTerminated.bind(this);
|
||||||
this.onConferenceJoined = this.onConferenceJoined.bind(this);
|
this.onConferenceJoined = this.onConferenceJoined.bind(this);
|
||||||
this.jitsiTimeout = null;
|
this.jitsiTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { navigation, user, baseUrl } = this.props;
|
const { route, user, baseUrl } = this.props;
|
||||||
const {
|
const {
|
||||||
name: displayName, id: userId, token, username
|
name: displayName, id: userId, token, username
|
||||||
} = user;
|
} = user;
|
||||||
|
@ -47,8 +48,8 @@ class JitsiMeetView extends React.Component {
|
||||||
displayName,
|
displayName,
|
||||||
avatar
|
avatar
|
||||||
};
|
};
|
||||||
const url = navigation.getParam('url');
|
const url = route.params?.url;
|
||||||
const onlyAudio = navigation.getParam('onlyAudio', false);
|
const onlyAudio = route.params?.onlyAudio ?? false;
|
||||||
if (onlyAudio) {
|
if (onlyAudio) {
|
||||||
JitsiMeet.audioCall(url, userInfo);
|
JitsiMeet.audioCall(url, userInfo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
|
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -16,10 +15,10 @@ import ListItem from '../../containers/ListItem';
|
||||||
import Separator from '../../containers/Separator';
|
import Separator from '../../containers/Separator';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
import { appStart as appStartAction, ROOT_LOADING, ROOT_INSIDE } from '../../actions/app';
|
||||||
import { appStart as appStartAction } from '../../actions';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const LANGUAGES = [
|
const LANGUAGES = [
|
||||||
{
|
{
|
||||||
|
@ -59,10 +58,9 @@ const LANGUAGES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
class LanguageView extends React.Component {
|
class LanguageView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Change_Language'),
|
title: I18n.t('Change_Language')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
|
@ -105,12 +103,12 @@ class LanguageView extends React.Component {
|
||||||
|
|
||||||
const { appStart } = this.props;
|
const { appStart } = this.props;
|
||||||
|
|
||||||
await appStart('loading', I18n.t('Change_language_loading'));
|
await appStart({ root: ROOT_LOADING, text: I18n.t('Change_language_loading') });
|
||||||
|
|
||||||
// shows loading for at least 300ms
|
// shows loading for at least 300ms
|
||||||
await Promise.all([this.changeLanguage(language), new Promise(resolve => setTimeout(resolve, 300))]);
|
await Promise.all([this.changeLanguage(language), new Promise(resolve => setTimeout(resolve, 300))]);
|
||||||
|
|
||||||
await appStart('inside');
|
await appStart({ root: ROOT_INSIDE });
|
||||||
}
|
}
|
||||||
|
|
||||||
changeLanguage = async(language) => {
|
changeLanguage = async(language) => {
|
||||||
|
@ -175,11 +173,7 @@ class LanguageView extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='language-view' theme={theme}>
|
||||||
style={[sharedStyles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID='language-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={LANGUAGES}
|
data={LANGUAGES}
|
||||||
|
@ -205,7 +199,7 @@ const mapStateToProps = state => ({
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
setUser: params => dispatch(setUserAction(params)),
|
setUser: params => dispatch(setUserAction(params)),
|
||||||
appStart: (...params) => dispatch(appStartAction(...params))
|
appStart: params => dispatch(appStartAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LanguageView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LanguageView));
|
||||||
|
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
Text, ScrollView, View, StyleSheet
|
Text, ScrollView, View, StyleSheet
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
@ -15,12 +14,9 @@ import StatusBar from '../containers/StatusBar';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import openLink from '../utils/openLink';
|
import openLink from '../utils/openLink';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
scroll: {
|
scroll: {
|
||||||
marginTop: 35,
|
marginTop: 35,
|
||||||
borderTopWidth: StyleSheet.hairlineWidth,
|
borderTopWidth: StyleSheet.hairlineWidth,
|
||||||
|
@ -52,11 +48,6 @@ Separator.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class LegalView extends React.Component {
|
class LegalView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
|
||||||
title: I18n.t('Legal'),
|
|
||||||
...themedHeader(screenProps.theme)
|
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
@ -88,14 +79,7 @@ class LegalView extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='legal-view' theme={theme}>
|
||||||
style={[
|
|
||||||
styles.container,
|
|
||||||
{ backgroundColor: themes[theme].auxiliaryBackground }
|
|
||||||
]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID='legal-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
|
@ -120,4 +104,8 @@ const mapStateToProps = state => ({
|
||||||
server: state.server.server
|
server: state.server.server
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LegalView.navigationOptions = {
|
||||||
|
title: I18n.t('Legal')
|
||||||
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(LegalView));
|
export default connect(mapStateToProps)(withTheme(LegalView));
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Text, StyleSheet, ScrollView } from 'react-native';
|
import { Text, StyleSheet, ScrollView } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
|
@ -18,6 +17,7 @@ import scrollPersistTaps from '../utils/scrollPersistTaps';
|
||||||
import { getUserSelector } from '../selectors/login';
|
import { getUserSelector } from '../selectors/login';
|
||||||
import Chips from '../containers/UIKit/MultiSelect/Chips';
|
import Chips from '../containers/UIKit/MultiSelect/Chips';
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -36,15 +36,17 @@ Title.propTypes = {
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
const LivechatEditView = ({ user, navigation, theme }) => {
|
const LivechatEditView = ({
|
||||||
|
user, navigation, route, theme
|
||||||
|
}) => {
|
||||||
const [customFields, setCustomFields] = useState({});
|
const [customFields, setCustomFields] = useState({});
|
||||||
const [availableUserTags, setAvailableUserTags] = useState([]);
|
const [availableUserTags, setAvailableUserTags] = useState([]);
|
||||||
|
|
||||||
const params = {};
|
const params = {};
|
||||||
const inputs = {};
|
const inputs = {};
|
||||||
|
|
||||||
const livechat = navigation.getParam('room', {});
|
const livechat = route.params?.room ?? {};
|
||||||
const visitor = navigation.getParam('roomUser', {});
|
const visitor = route.params?.roomUser ?? {};
|
||||||
|
|
||||||
const getCustomFields = async() => {
|
const getCustomFields = async() => {
|
||||||
const result = await RocketChat.getCustomFields();
|
const result = await RocketChat.getCustomFields();
|
||||||
|
@ -148,8 +150,8 @@ const LivechatEditView = ({ user, navigation, theme }) => {
|
||||||
contentContainerStyle={sharedStyles.container}
|
contentContainerStyle={sharedStyles.container}
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps}>
|
<ScrollView {...scrollPersistTaps} style={styles.container}>
|
||||||
<SafeAreaView style={[sharedStyles.container, styles.container]} forceInset={{ vertical: 'never' }}>
|
<SafeAreaView theme={theme}>
|
||||||
<Title
|
<Title
|
||||||
title={visitor?.username}
|
title={visitor?.username}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -271,6 +273,7 @@ const LivechatEditView = ({ user, navigation, theme }) => {
|
||||||
LivechatEditView.propTypes = {
|
LivechatEditView.propTypes = {
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
LivechatEditView.navigationOptions = ({
|
LivechatEditView.navigationOptions = ({
|
||||||
|
|
|
@ -13,7 +13,6 @@ import I18n from '../i18n';
|
||||||
import { LegalButton } from '../containers/HeaderButton';
|
import { LegalButton } from '../containers/HeaderButton';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
import TextInput from '../containers/TextInput';
|
import TextInput from '../containers/TextInput';
|
||||||
import { loginRequest as loginRequestAction } from '../actions/login';
|
import { loginRequest as loginRequestAction } from '../actions/login';
|
||||||
|
@ -51,14 +50,10 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class LoginView extends React.Component {
|
class LoginView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ route, navigation }) => ({
|
||||||
const title = navigation.getParam('title', 'Rocket.Chat');
|
title: route.params?.title ?? 'Rocket.Chat',
|
||||||
return {
|
headerRight: () => <LegalButton testID='login-view-more' navigation={navigation} />
|
||||||
...themedHeader(screenProps.theme),
|
})
|
||||||
title,
|
|
||||||
headerRight: <LegalButton testID='login-view-more' navigation={navigation} />
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
@ -205,11 +200,11 @@ class LoginView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { Accounts_ShowFormLogin, theme } = this.props;
|
const { Accounts_ShowFormLogin, theme, navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<FormContainer theme={theme} testID='login-view'>
|
<FormContainer theme={theme} testID='login-view'>
|
||||||
<FormContainerInner>
|
<FormContainerInner>
|
||||||
<LoginServices separator={Accounts_ShowFormLogin} />
|
<LoginServices separator={Accounts_ShowFormLogin} navigation={navigation} />
|
||||||
{this.renderUserForm()}
|
{this.renderUserForm()}
|
||||||
</FormContainerInner>
|
</FormContainerInner>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
|
@ -6,23 +6,21 @@ 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 { themedHeader } from '../utils/navigation';
|
|
||||||
|
|
||||||
class MarkdownTableView extends React.Component {
|
class MarkdownTableView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
title: I18n.t('Table')
|
title: I18n.t('Table')
|
||||||
});
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { navigation, theme } = this.props;
|
const { route, theme } = this.props;
|
||||||
const renderRows = navigation.getParam('renderRows');
|
const renderRows = route.params?.renderRows;
|
||||||
const tableWidth = navigation.getParam('tableWidth');
|
const tableWidth = route.params?.tableWidth;
|
||||||
|
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import ActionSheet from 'react-native-action-sheet';
|
import ActionSheet from 'react-native-action-sheet';
|
||||||
|
|
||||||
|
@ -15,26 +14,24 @@ import StatusBar from '../../containers/StatusBar';
|
||||||
import getFileUrlFromMessage from '../../lib/methods/helpers/getFileUrlFromMessage';
|
import getFileUrlFromMessage from '../../lib/methods/helpers/getFileUrlFromMessage';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const ACTION_INDEX = 0;
|
const ACTION_INDEX = 0;
|
||||||
const CANCEL_INDEX = 1;
|
const CANCEL_INDEX = 1;
|
||||||
|
|
||||||
class MessagesView extends React.Component {
|
class MessagesView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => ({
|
static navigationOptions = ({ route }) => ({
|
||||||
title: I18n.t(navigation.state.params.name),
|
title: I18n.t(route.params?.name)
|
||||||
...themedHeader(screenProps.theme)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
customEmojis: PropTypes.object,
|
customEmojis: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
split: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -44,9 +41,9 @@ class MessagesView extends React.Component {
|
||||||
messages: [],
|
messages: [],
|
||||||
fileLoading: true
|
fileLoading: true
|
||||||
};
|
};
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
this.t = props.navigation.getParam('t');
|
this.t = props.route.params?.t;
|
||||||
this.content = this.defineMessagesViewContent(props.navigation.getParam('name'));
|
this.content = this.defineMessagesViewContent(props.route.params?.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -223,12 +220,8 @@ class MessagesView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
showAttachment = (attachment) => {
|
showAttachment = (attachment) => {
|
||||||
const { navigation, split } = this.props;
|
const { navigation } = this.props;
|
||||||
let params = { attachment };
|
navigation.navigate('AttachmentView', { attachment });
|
||||||
if (split) {
|
|
||||||
params = { ...params, from: 'MessagesView' };
|
|
||||||
}
|
|
||||||
navigation.navigate('AttachmentView', params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLongPress = (message) => {
|
onLongPress = (message) => {
|
||||||
|
@ -295,12 +288,9 @@ class MessagesView extends React.Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
style={[
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
styles.list,
|
|
||||||
{ backgroundColor: themes[theme].backgroundColor }
|
|
||||||
]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID={this.content.testID}
|
testID={this.content.testID}
|
||||||
|
theme={theme}
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<FlatList
|
<FlatList
|
||||||
|
@ -322,4 +312,4 @@ const mapStateToProps = state => ({
|
||||||
customEmojis: state.customEmojis
|
customEmojis: state.customEmojis
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withSplit(withTheme(MessagesView)));
|
export default connect(mapStateToProps)(withTheme(MessagesView));
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { connect } from 'react-redux';
|
||||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||||||
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { CustomHeaderButtons, Item } from '../containers/HeaderButton';
|
import { CustomHeaderButtons, Item } from '../containers/HeaderButton';
|
||||||
|
@ -57,41 +56,18 @@ const mapElementToState = ({ element, blockId, elements = [] }) => {
|
||||||
const reduceState = (obj, el) => (Array.isArray(el[0]) ? { ...obj, ...Object.fromEntries(el) } : { ...obj, [el[0]]: el[1] });
|
const reduceState = (obj, el) => (Array.isArray(el[0]) ? { ...obj, ...Object.fromEntries(el) } : { ...obj, [el[0]]: el[1] });
|
||||||
|
|
||||||
class ModalBlockView extends React.Component {
|
class ModalBlockView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ route }) => {
|
||||||
const { theme, closeModal } = screenProps;
|
const data = route.params?.data;
|
||||||
const data = navigation.getParam('data');
|
|
||||||
const cancel = navigation.getParam('cancel', () => {});
|
|
||||||
const submitting = navigation.getParam('submitting', false);
|
|
||||||
const { view } = data;
|
const { view } = data;
|
||||||
const { title, submit, close } = view;
|
const { title } = view;
|
||||||
return {
|
return {
|
||||||
title: textParser([title]),
|
title: textParser([title])
|
||||||
...themedHeader(theme),
|
|
||||||
headerLeft: close ? (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item
|
|
||||||
title={textParser([close.text])}
|
|
||||||
style={styles.submit}
|
|
||||||
onPress={!submitting && (() => cancel({ closeModal }))}
|
|
||||||
testID='close-modal-uikit'
|
|
||||||
/>
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
) : null,
|
|
||||||
headerRight: submit ? (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item
|
|
||||||
title={textParser([submit.text])}
|
|
||||||
style={styles.submit}
|
|
||||||
onPress={!submitting && (navigation.getParam('submit', () => {}))}
|
|
||||||
testID='submit-modal-uikit'
|
|
||||||
/>
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
) : null
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
language: PropTypes.string,
|
language: PropTypes.string,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
|
@ -103,21 +79,18 @@ class ModalBlockView extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.submitting = false;
|
this.submitting = false;
|
||||||
const { navigation } = props;
|
const data = props.route.params?.data;
|
||||||
const data = navigation.getParam('data');
|
|
||||||
this.values = data.view.blocks.filter(filterInputFields).map(mapElementToState).reduce(reduceState, {});
|
this.values = data.view.blocks.filter(filterInputFields).map(mapElementToState).reduce(reduceState, {});
|
||||||
this.state = {
|
this.state = {
|
||||||
data,
|
data,
|
||||||
loading: false
|
loading: false
|
||||||
};
|
};
|
||||||
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { data } = this.state;
|
const { data } = this.state;
|
||||||
const { navigation } = this.props;
|
|
||||||
const { viewId } = data;
|
const { viewId } = data;
|
||||||
navigation.setParams({ submit: this.submit, cancel: this.cancel });
|
|
||||||
|
|
||||||
EventEmitter.addEventListener(viewId, this.handleUpdate);
|
EventEmitter.addEventListener(viewId, this.handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,9 +106,9 @@ class ModalBlockView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { navigation } = this.props;
|
const { navigation, route } = this.props;
|
||||||
const oldData = prevProps.navigation.getParam('data', {});
|
const oldData = prevProps.route.params?.data ?? {};
|
||||||
const newData = navigation.getParam('data', {});
|
const newData = route.params?.data ?? {};
|
||||||
if (oldData.viewId !== newData.viewId) {
|
if (oldData.viewId !== newData.viewId) {
|
||||||
navigation.push('ModalBlockView', { data: newData });
|
navigation.push('ModalBlockView', { data: newData });
|
||||||
}
|
}
|
||||||
|
@ -147,14 +120,43 @@ class ModalBlockView extends React.Component {
|
||||||
EventEmitter.removeListener(viewId, this.handleUpdate);
|
EventEmitter.removeListener(viewId, this.handleUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdate = ({ type, ...data }) => {
|
setHeader = () => {
|
||||||
|
const { data } = this.state;
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
const { view } = data;
|
||||||
|
const { title, close, submit } = view;
|
||||||
|
navigation.setOptions({
|
||||||
|
title: textParser([title]),
|
||||||
|
headerLeft: close ? () => (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item
|
||||||
|
title={textParser([close.text])}
|
||||||
|
style={styles.submit}
|
||||||
|
onPress={this.cancel}
|
||||||
|
testID='close-modal-uikit'
|
||||||
|
/>
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
) : null,
|
||||||
|
headerRight: submit ? () => (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item
|
||||||
|
title={textParser([submit.text])}
|
||||||
|
style={styles.submit}
|
||||||
|
onPress={this.submit}
|
||||||
|
testID='submit-modal-uikit'
|
||||||
|
/>
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
) : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUpdate = ({ type, ...data }) => {
|
||||||
if ([MODAL_ACTIONS.ERRORS].includes(type)) {
|
if ([MODAL_ACTIONS.ERRORS].includes(type)) {
|
||||||
const { errors } = data;
|
const { errors } = data;
|
||||||
this.setState({ errors });
|
this.setState({ errors });
|
||||||
} else {
|
} else {
|
||||||
this.setState({ data });
|
this.setState({ data });
|
||||||
navigation.setParams({ data });
|
this.setHeader();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,8 +189,11 @@ class ModalBlockView extends React.Component {
|
||||||
|
|
||||||
submit = async() => {
|
submit = async() => {
|
||||||
const { data } = this.state;
|
const { data } = this.state;
|
||||||
const { navigation } = this.props;
|
if (this.submitting) {
|
||||||
navigation.setParams({ submitting: true });
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitting = true;
|
||||||
|
|
||||||
const { appId, viewId } = data;
|
const { appId, viewId } = data;
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
@ -207,7 +212,7 @@ class ModalBlockView extends React.Component {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
navigation.setParams({ submitting: false });
|
this.submitting = false;
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
View, StyleSheet, FlatList, Text
|
View, StyleSheet, FlatList, Text
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import { orderBy } from 'lodash';
|
import { orderBy } from 'lodash';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
@ -22,16 +21,13 @@ import { CloseModalButton } 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 { themedHeader } from '../utils/navigation';
|
|
||||||
import { getUserSelector } from '../selectors/login';
|
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';
|
||||||
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
safeAreaView: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
separator: {
|
separator: {
|
||||||
marginLeft: 60
|
marginLeft: 60
|
||||||
},
|
},
|
||||||
|
@ -54,9 +50,8 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class NewMessageView extends React.Component {
|
class NewMessageView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => ({
|
static navigationOptions = ({ navigation }) => ({
|
||||||
...themedHeader(screenProps.theme),
|
headerLeft: () => <CloseModalButton navigation={navigation} testID='new-message-view-close' />,
|
||||||
headerLeft: <CloseModalButton navigation={navigation} testID='new-message-view-close' />,
|
|
||||||
title: I18n.t('New_Message')
|
title: I18n.t('New_Message')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -69,7 +64,8 @@ class NewMessageView extends React.Component {
|
||||||
}),
|
}),
|
||||||
createChannel: PropTypes.func,
|
createChannel: PropTypes.func,
|
||||||
maxUsers: PropTypes.number,
|
maxUsers: PropTypes.number,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -150,6 +146,14 @@ class NewMessageView extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goRoom = (item) => {
|
||||||
|
const { isMasterDetail, navigation } = this.props;
|
||||||
|
if (isMasterDetail) {
|
||||||
|
navigation.pop();
|
||||||
|
}
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
|
}
|
||||||
|
|
||||||
renderButton = ({
|
renderButton = ({
|
||||||
onPress, testID, title, icon, first
|
onPress, testID, title, icon, first
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -226,7 +230,7 @@ class NewMessageView extends React.Component {
|
||||||
<UserItem
|
<UserItem
|
||||||
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={() => goRoom(item)}
|
onPress={() => this.goRoom(item)}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
testID={`new-message-view-item-${ item.name }`}
|
testID={`new-message-view-item-${ item.name }`}
|
||||||
style={style}
|
style={style}
|
||||||
|
@ -256,11 +260,7 @@ class NewMessageView extends React.Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='new-message-view' theme={theme}>
|
||||||
style={[styles.safeAreaView, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
|
||||||
forceInset={{ vertical: 'never' }}
|
|
||||||
testID='new-message-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
@ -269,6 +269,7 @@ class NewMessageView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
|
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
|
||||||
user: getUserSelector(state)
|
user: getUserSelector(state)
|
||||||
|
|
|
@ -12,10 +12,7 @@ import { encode } from 'base-64';
|
||||||
import parse from 'url-parse';
|
import parse from 'url-parse';
|
||||||
|
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
import {
|
import { selectServerRequest, serverRequest } from '../actions/server';
|
||||||
selectServerRequest, serverRequest, serverInitAdd, serverFinishAdd
|
|
||||||
} from '../actions/server';
|
|
||||||
import { appStart as appStartAction } from '../actions';
|
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import TextInput from '../containers/TextInput';
|
import TextInput from '../containers/TextInput';
|
||||||
|
@ -28,7 +25,6 @@ import log from '../utils/log';
|
||||||
import { animateNextTransition } from '../utils/layoutAnimation';
|
import { animateNextTransition } from '../utils/layoutAnimation';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';
|
import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import { CloseModalButton } from '../containers/HeaderButton';
|
import { CloseModalButton } from '../containers/HeaderButton';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -65,14 +61,8 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class NewServerView extends React.Component {
|
class NewServerView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps, navigation }) => {
|
static navigationOptions = {
|
||||||
const previousServer = navigation.getParam('previousServer', null);
|
title: I18n.t('Workspaces')
|
||||||
const close = navigation.getParam('close', () => {});
|
|
||||||
return {
|
|
||||||
headerLeft: previousServer ? <CloseModalButton navigation={navigation} onPress={close} testID='new-server-view-close' /> : undefined,
|
|
||||||
title: I18n.t('Workspaces'),
|
|
||||||
...themedHeader(screenProps.theme)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -81,15 +71,17 @@ class NewServerView extends React.Component {
|
||||||
connecting: PropTypes.bool.isRequired,
|
connecting: PropTypes.bool.isRequired,
|
||||||
connectServer: PropTypes.func.isRequired,
|
connectServer: PropTypes.func.isRequired,
|
||||||
selectServer: PropTypes.func.isRequired,
|
selectServer: PropTypes.func.isRequired,
|
||||||
currentServer: PropTypes.string,
|
adding: PropTypes.bool,
|
||||||
initAdd: PropTypes.func,
|
previousServer: PropTypes.string
|
||||||
finishAdd: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.previousServer = props.navigation.getParam('previousServer');
|
if (props.adding) {
|
||||||
props.navigation.setParams({ close: this.close, previousServer: this.previousServer });
|
props.navigation.setOptions({
|
||||||
|
headerLeft: () => <CloseModalButton navigation={props.navigation} onPress={this.close} testID='new-server-view-close' />
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel
|
// Cancel
|
||||||
this.options = [I18n.t('Cancel')];
|
this.options = [I18n.t('Cancel')];
|
||||||
|
@ -108,21 +100,14 @@ class NewServerView extends React.Component {
|
||||||
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
|
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { initAdd } = this.props;
|
|
||||||
if (this.previousServer) {
|
|
||||||
initAdd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
|
EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
|
||||||
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
|
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBackPress = () => {
|
handleBackPress = () => {
|
||||||
const { navigation } = this.props;
|
const { navigation, previousServer } = this.props;
|
||||||
if (navigation.isFocused() && this.previousServer) {
|
if (navigation.isFocused() && previousServer) {
|
||||||
this.close();
|
this.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -134,11 +119,8 @@ class NewServerView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
close = () => {
|
close = () => {
|
||||||
const { selectServer, currentServer, finishAdd } = this.props;
|
const { selectServer, previousServer } = this.props;
|
||||||
if (this.previousServer !== currentServer) {
|
selectServer(previousServer);
|
||||||
selectServer(this.previousServer);
|
|
||||||
}
|
|
||||||
finishAdd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewServerEvent = (event) => {
|
handleNewServerEvent = (event) => {
|
||||||
|
@ -344,15 +326,14 @@ class NewServerView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
connecting: state.server.connecting
|
connecting: state.server.connecting,
|
||||||
|
adding: state.server.adding,
|
||||||
|
previousServer: state.server.previousServer
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)),
|
connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)),
|
||||||
initAdd: () => dispatch(serverInitAdd()),
|
selectServer: server => dispatch(selectServerRequest(server))
|
||||||
finishAdd: () => dispatch(serverFinishAdd()),
|
|
||||||
selectServer: server => dispatch(selectServerRequest(server)),
|
|
||||||
appStart: root => dispatch(appStartAction(root))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView));
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
View, ScrollView, Switch, Text
|
View, ScrollView, Switch, Text
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
||||||
|
@ -13,11 +12,10 @@ import Separator from '../../containers/Separator';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import sharedStyles from '../Styles';
|
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const SectionTitle = React.memo(({ title, theme }) => (
|
const SectionTitle = React.memo(({ title, theme }) => (
|
||||||
<Text
|
<Text
|
||||||
|
@ -140,21 +138,21 @@ const OPTIONS = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotificationPreferencesView extends React.Component {
|
class NotificationPreferencesView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Notification_Preferences'),
|
title: I18n.t('Notification_Preferences')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
const room = props.navigation.getParam('room');
|
const room = props.route.params?.room;
|
||||||
this.state = {
|
this.state = {
|
||||||
room: room || {}
|
room: room || {}
|
||||||
};
|
};
|
||||||
|
@ -245,7 +243,7 @@ class NotificationPreferencesView extends React.Component {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={sharedStyles.container} testID='notification-preference-view' forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='notification-preference-view' theme={theme}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
import Orientation from 'react-native-orientation-locker';
|
||||||
|
|
||||||
import { appStart as appStartAction } from '../../actions';
|
import { appStart as appStartAction, ROOT_BACKGROUND } from '../../actions/app';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import Button from '../../containers/Button';
|
import Button from '../../containers/Button';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -16,9 +16,9 @@ import { withTheme } from '../../theme';
|
||||||
import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
|
||||||
|
|
||||||
class OnboardingView extends React.Component {
|
class OnboardingView extends React.Component {
|
||||||
static navigationOptions = () => ({
|
static navigationOptions = {
|
||||||
header: null
|
headerShown: false
|
||||||
})
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
@ -28,12 +28,23 @@ class OnboardingView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
|
|
||||||
if (!isTablet) {
|
if (!isTablet) {
|
||||||
Orientation.lockToPortrait();
|
Orientation.lockToPortrait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { navigation } = this.props;
|
||||||
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
|
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
|
||||||
|
});
|
||||||
|
this.unsubscribeBlur = navigation.addListener('blur', () => {
|
||||||
|
if (this.backHandler && this.backHandler.remove) {
|
||||||
|
this.backHandler.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
if (theme !== nextProps.theme) {
|
if (theme !== nextProps.theme) {
|
||||||
|
@ -43,12 +54,17 @@ class OnboardingView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
|
if (this.unsubscribeFocus) {
|
||||||
|
this.unsubscribeFocus();
|
||||||
|
}
|
||||||
|
if (this.unsubscribeBlur) {
|
||||||
|
this.unsubscribeBlur();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBackPress = () => {
|
handleBackPress = () => {
|
||||||
const { appStart } = this.props;
|
const { appStart } = this.props;
|
||||||
appStart('background');
|
appStart({ root: ROOT_BACKGROUND });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +114,7 @@ class OnboardingView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
appStart: root => dispatch(appStartAction(root))
|
appStart: params => dispatch(appStartAction(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(withTheme(OnboardingView));
|
export default connect(null, mapDispatchToProps)(withTheme(OnboardingView));
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
|
@ -57,29 +56,29 @@ Item.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class PickerView extends React.PureComponent {
|
class PickerView extends React.PureComponent {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => ({
|
static navigationOptions = ({ route }) => ({
|
||||||
title: navigation.getParam('title', I18n.t('Select_an_option')),
|
title: route.params?.title ?? I18n.t('Select_an_option')
|
||||||
...themedHeader(screenProps.theme)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const data = props.navigation.getParam('data', []);
|
const data = props.route.params?.data ?? [];
|
||||||
const value = props.navigation.getParam('value');
|
const value = props.route.params?.value;
|
||||||
this.state = { data, value };
|
this.state = { data, value };
|
||||||
|
|
||||||
this.onSearch = props.navigation.getParam('onChangeText');
|
this.onSearch = props.route.params?.onChangeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeValue = (value) => {
|
onChangeValue = (value) => {
|
||||||
const { navigation } = this.props;
|
const { navigation, route } = this.props;
|
||||||
const goBack = navigation.getParam('goBack', true);
|
const goBack = route.params?.goBack ?? true;
|
||||||
const onChange = navigation.getParam('onChangeValue', () => {});
|
const onChange = route.params?.onChangeValue ?? (() => {});
|
||||||
onChange(value);
|
onChange(value);
|
||||||
if (goBack) {
|
if (goBack) {
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
|
|
|
@ -6,8 +6,6 @@ import prompt from 'react-native-prompt-android';
|
||||||
import SHA256 from 'js-sha256';
|
import SHA256 from 'js-sha256';
|
||||||
import ImagePicker from 'react-native-image-crop-picker';
|
import ImagePicker from 'react-native-image-crop-picker';
|
||||||
import RNPickerSelect from 'react-native-picker-select';
|
import RNPickerSelect from 'react-native-picker-select';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import { HeaderBackButton } from 'react-navigation-stack';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
|
@ -30,22 +28,19 @@ import { DrawerButton } 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 { themedHeader } from '../../utils/navigation';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
class ProfileView extends React.Component {
|
class ProfileView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => ({
|
static navigationOptions = ({ navigation, isMasterDetail }) => {
|
||||||
...themedHeader(screenProps.theme),
|
const options = {
|
||||||
headerLeft: screenProps.split ? (
|
|
||||||
<HeaderBackButton
|
|
||||||
onPress={() => navigation.navigate('SettingsView')}
|
|
||||||
tintColor={themes[screenProps.theme].headerTintColor}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<DrawerButton navigation={navigation} />
|
|
||||||
),
|
|
||||||
title: I18n.t('Profile')
|
title: I18n.t('Profile')
|
||||||
})
|
};
|
||||||
|
if (!isMasterDetail) {
|
||||||
|
options.headerLeft = () => <DrawerButton navigation={navigation} />;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
|
@ -440,7 +435,7 @@ class ProfileView extends React.Component {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<SafeAreaView style={sharedStyles.container} testID='profile-view' forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='profile-view' theme={theme}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={sharedStyles.containerScrollView}
|
contentContainerStyle={sharedStyles.containerScrollView}
|
||||||
testID='profile-view-list'
|
testID='profile-view-list'
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text } from 'react-native';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -13,18 +12,17 @@ import I18n from '../../i18n';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
class ReadReceiptView extends React.Component {
|
class ReadReceiptView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Read_Receipt'),
|
title: I18n.t('Read_Receipt')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
route: PropTypes.object,
|
||||||
Message_TimeFormat: PropTypes.string,
|
Message_TimeFormat: PropTypes.string,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
|
@ -33,7 +31,7 @@ class ReadReceiptView extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.messageId = props.navigation.getParam('messageId');
|
this.messageId = props.route.params?.messageId;
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
receipts: []
|
receipts: []
|
||||||
|
@ -135,11 +133,7 @@ class ReadReceiptView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView
|
<SafeAreaView testID='read-receipt-view' theme={theme}>
|
||||||
style={[styles.container, { backgroundColor: themes[theme].chatComponentBackground }]}
|
|
||||||
forceInset={{ bottom: 'always' }}
|
|
||||||
testID='read-receipt-view'
|
|
||||||
>
|
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<View>
|
<View>
|
||||||
{loading
|
{loading
|
||||||
|
|
|
@ -28,9 +28,6 @@ export default StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
padding: 10
|
padding: 10
|
||||||
},
|
},
|
||||||
container: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
list: {
|
list: {
|
||||||
...sharedStyles.separatorVertical,
|
...sharedStyles.separatorVertical,
|
||||||
marginVertical: 10
|
marginVertical: 10
|
||||||
|
|
|
@ -13,7 +13,6 @@ import I18n from '../i18n';
|
||||||
import { LegalButton } from '../containers/HeaderButton';
|
import { LegalButton } from '../containers/HeaderButton';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import { themedHeader } from '../utils/navigation';
|
|
||||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
import TextInput from '../containers/TextInput';
|
import TextInput from '../containers/TextInput';
|
||||||
import isValidEmail from '../utils/isValidEmail';
|
import isValidEmail from '../utils/isValidEmail';
|
||||||
|
@ -53,14 +52,10 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
class RegisterView extends React.Component {
|
class RegisterView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ route, navigation }) => ({
|
||||||
const title = navigation.getParam('title', 'Rocket.Chat');
|
title: route.params?.title ?? 'Rocket.Chat',
|
||||||
return {
|
headerRight: () => <LegalButton testID='register-view-more' navigation={navigation} />
|
||||||
...themedHeader(screenProps.theme),
|
});
|
||||||
title,
|
|
||||||
headerRight: <LegalButton navigation={navigation} testID='register-view-more' />
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
@ -228,11 +223,11 @@ class RegisterView extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { saving } = this.state;
|
const { saving } = this.state;
|
||||||
const { theme, showLoginButton } = this.props;
|
const { theme, showLoginButton, navigation } = this.props;
|
||||||
return (
|
return (
|
||||||
<FormContainer theme={theme} testID='register-view'>
|
<FormContainer theme={theme} testID='register-view'>
|
||||||
<FormContainerInner>
|
<FormContainerInner>
|
||||||
<LoginServices />
|
<LoginServices navigation={navigation} />
|
||||||
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>
|
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Sign_Up')}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label='Name'
|
label='Name'
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
View, SectionList, Text, Alert, Share
|
View, SectionList, Text, Alert, Share
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
|
@ -24,20 +23,19 @@ import DisclosureIndicator from '../../containers/DisclosureIndicator';
|
||||||
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 { themedHeader } from '../../utils/navigation';
|
|
||||||
import { CloseModalButton } from '../../containers/HeaderButton';
|
import { CloseModalButton } from '../../containers/HeaderButton';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import Markdown from '../../containers/markdown';
|
import Markdown from '../../containers/markdown';
|
||||||
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
|
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
class RoomActionsView extends React.Component {
|
class RoomActionsView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
static navigationOptions = ({ navigation, isMasterDetail }) => {
|
||||||
const options = {
|
const options = {
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
title: I18n.t('Actions')
|
title: I18n.t('Actions')
|
||||||
};
|
};
|
||||||
if (screenProps.split) {
|
if (isMasterDetail) {
|
||||||
options.headerLeft = <CloseModalButton navigation={navigation} testID='room-actions-view-close' />;
|
options.headerLeft = () => <CloseModalButton navigation={navigation} testID='room-actions-view-close' />;
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +43,7 @@ class RoomActionsView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
token: PropTypes.string
|
token: PropTypes.string
|
||||||
|
@ -59,10 +58,10 @@ class RoomActionsView extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
const room = props.navigation.getParam('room');
|
const room = props.route.params?.room;
|
||||||
const member = props.navigation.getParam('member');
|
const member = props.route.params?.member;
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
this.t = props.navigation.getParam('t');
|
this.t = props.route.params?.t;
|
||||||
this.state = {
|
this.state = {
|
||||||
room: room || { rid: this.rid, t: this.t },
|
room: room || { rid: this.rid, t: this.t },
|
||||||
membersCount: 0,
|
membersCount: 0,
|
||||||
|
@ -647,7 +646,7 @@ class RoomActionsView extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} testID='room-actions-view' forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='room-actions-view' theme={theme}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<SectionList
|
<SectionList
|
||||||
contentContainerStyle={[styles.contentContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
contentContainerStyle={[styles.contentContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import {
|
||||||
Text, View, ScrollView, TouchableOpacity, Keyboard, Alert
|
Text, View, ScrollView, TouchableOpacity, Keyboard, Alert
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
|
@ -27,11 +26,11 @@ import random from '../../utils/random';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
|
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
|
||||||
import { MessageTypeValues } from '../../utils/messageTypes';
|
import { MessageTypeValues } from '../../utils/messageTypes';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
|
||||||
const PERMISSION_SET_READONLY = 'set-readonly';
|
const PERMISSION_SET_READONLY = 'set-readonly';
|
||||||
const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
|
const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
|
||||||
|
@ -49,13 +48,12 @@ const PERMISSIONS_ARRAY = [
|
||||||
];
|
];
|
||||||
|
|
||||||
class RoomInfoEditView extends React.Component {
|
class RoomInfoEditView extends React.Component {
|
||||||
static navigationOptions = ({ screenProps }) => ({
|
static navigationOptions = {
|
||||||
title: I18n.t('Room_Info_Edit'),
|
title: I18n.t('Room_Info_Edit')
|
||||||
...themedHeader(screenProps.theme)
|
}
|
||||||
})
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
route: PropTypes.object,
|
||||||
deleteRoom: PropTypes.func,
|
deleteRoom: PropTypes.func,
|
||||||
serverVersion: PropTypes.string,
|
serverVersion: PropTypes.string,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
@ -101,8 +99,8 @@ class RoomInfoEditView extends React.Component {
|
||||||
|
|
||||||
// eslint-disable-next-line react/sort-comp
|
// eslint-disable-next-line react/sort-comp
|
||||||
loadRoom = async() => {
|
loadRoom = async() => {
|
||||||
const { navigation } = this.props;
|
const { route } = this.props;
|
||||||
const rid = navigation.getParam('rid', null);
|
const rid = route.params?.rid;
|
||||||
if (!rid) {
|
if (!rid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -349,12 +347,16 @@ class RoomInfoEditView extends React.Component {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
|
<SafeAreaView
|
||||||
|
testID='room-info-edit-view'
|
||||||
|
theme={theme}
|
||||||
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
|
>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={sharedStyles.containerScrollView}
|
contentContainerStyle={sharedStyles.containerScrollView}
|
||||||
testID='room-info-edit-view-list'
|
testID='room-info-edit-view-list'
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<SafeAreaView style={sharedStyles.container} testID='room-info-edit-view' forceInset={{ vertical: 'never' }}>
|
|
||||||
<RCTextInput
|
<RCTextInput
|
||||||
inputRef={(e) => { this.name = e; }}
|
inputRef={(e) => { this.name = e; }}
|
||||||
label={I18n.t('Name')}
|
label={I18n.t('Name')}
|
||||||
|
@ -542,8 +544,8 @@ class RoomInfoEditView extends React.Component {
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Loading visible={saving} />
|
<Loading visible={saving} />
|
||||||
</SafeAreaView>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||||
import { View, Text, ScrollView } from 'react-native';
|
import { View, Text, ScrollView } from 'react-native';
|
||||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import UAParser from 'ua-parser-js';
|
import UAParser from 'ua-parser-js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
@ -16,21 +15,21 @@ import sharedStyles from '../Styles';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { CustomHeaderButtons } from '../../containers/HeaderButton';
|
import { CustomHeaderButtons, CloseModalButton } from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { withSplit } from '../../split';
|
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import Markdown from '../../containers/markdown';
|
import Markdown from '../../containers/markdown';
|
||||||
import Navigation from '../../lib/Navigation';
|
|
||||||
|
|
||||||
import Livechat from './Livechat';
|
import Livechat from './Livechat';
|
||||||
import Channel from './Channel';
|
import Channel from './Channel';
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
import Direct from './Direct';
|
import Direct from './Direct';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
import { goRoom } from '../../utils/goRoom';
|
||||||
|
import Navigation from '../../lib/Navigation';
|
||||||
|
|
||||||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
const PERMISSION_EDIT_ROOM = 'edit-room';
|
||||||
const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd'
|
const getRoomTitle = (room, type, name, username, statusText, theme) => (type === 'd'
|
||||||
|
@ -50,50 +49,29 @@ const getRoomTitle = (room, type, name, username, statusText, theme) => (type ==
|
||||||
);
|
);
|
||||||
|
|
||||||
class RoomInfoView extends React.Component {
|
class RoomInfoView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
|
||||||
const t = navigation.getParam('t');
|
|
||||||
const rid = navigation.getParam('rid');
|
|
||||||
const room = navigation.getParam('room');
|
|
||||||
const roomUser = navigation.getParam('roomUser');
|
|
||||||
const showEdit = navigation.getParam('showEdit', t === 'l');
|
|
||||||
return {
|
|
||||||
title: t === 'd' ? I18n.t('User_Info') : I18n.t('Room_Info'),
|
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
headerRight: showEdit
|
|
||||||
? (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item
|
|
||||||
iconName='edit'
|
|
||||||
onPress={() => navigation.navigate(t === 'l' ? 'LivechatEditView' : 'RoomInfoEditView', { rid, room, roomUser })}
|
|
||||||
testID='room-info-view-edit-button'
|
|
||||||
/>
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
)
|
|
||||||
: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
token: PropTypes.string
|
token: PropTypes.string
|
||||||
}),
|
}),
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
rooms: PropTypes.array,
|
rooms: PropTypes.array,
|
||||||
split: PropTypes.bool,
|
theme: PropTypes.string,
|
||||||
theme: PropTypes.string
|
isMasterDetail: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const room = props.navigation.getParam('room');
|
const room = props.route.params?.room;
|
||||||
const roomUser = props.navigation.getParam('member');
|
const roomUser = props.route.params?.member;
|
||||||
this.rid = props.navigation.getParam('rid');
|
this.rid = props.route.params?.rid;
|
||||||
this.t = props.navigation.getParam('t');
|
this.t = props.route.params?.t;
|
||||||
this.state = {
|
this.state = {
|
||||||
room: room || { rid: this.rid, t: this.t },
|
room: room || { rid: this.rid, t: this.t },
|
||||||
roomUser: roomUser || {}
|
roomUser: roomUser || {},
|
||||||
|
showEdit: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,9 +81,10 @@ class RoomInfoView extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
this.loadRoom();
|
this.loadRoom();
|
||||||
}
|
}
|
||||||
|
this.setHeader();
|
||||||
|
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
this.willFocusListener = navigation.addListener('willFocus', () => {
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
if (this.isLivechat) {
|
if (this.isLivechat) {
|
||||||
this.loadVisitor();
|
this.loadVisitor();
|
||||||
}
|
}
|
||||||
|
@ -116,11 +95,34 @@ class RoomInfoView extends React.Component {
|
||||||
if (this.subscription && this.subscription.unsubscribe) {
|
if (this.subscription && this.subscription.unsubscribe) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
if (this.willFocusListener && this.willFocusListener.remove) {
|
if (this.unsubscribeFocus) {
|
||||||
this.willFocusListener.remove();
|
this.unsubscribeFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHeader = () => {
|
||||||
|
const { roomUser, room, showEdit } = this.state;
|
||||||
|
const { navigation, route } = this.props;
|
||||||
|
const t = route.params?.t;
|
||||||
|
const rid = route.params?.rid;
|
||||||
|
const showCloseModal = route.params?.showCloseModal;
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: showCloseModal ? () => <CloseModalButton navigation={navigation} /> : undefined,
|
||||||
|
title: t === 'd' ? I18n.t('User_Info') : I18n.t('Room_Info'),
|
||||||
|
headerRight: showEdit
|
||||||
|
? () => (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item
|
||||||
|
iconName='edit'
|
||||||
|
onPress={() => navigation.navigate(t === 'l' ? 'LivechatEditView' : 'RoomInfoEditView', { rid, room, roomUser })}
|
||||||
|
testID='room-info-view-edit-button'
|
||||||
|
/>
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
get isDirect() {
|
get isDirect() {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
return room.t === 'd';
|
return room.t === 'd';
|
||||||
|
@ -147,8 +149,6 @@ class RoomInfoView extends React.Component {
|
||||||
|
|
||||||
loadVisitor = async() => {
|
loadVisitor = async() => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { navigation } = this.props;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await RocketChat.getVisitorInfo(room?.visitor?._id);
|
const result = await RocketChat.getVisitorInfo(room?.visitor?._id);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
@ -159,8 +159,7 @@ class RoomInfoView extends React.Component {
|
||||||
visitor.os = `${ ua.getOS().name } ${ ua.getOS().version }`;
|
visitor.os = `${ ua.getOS().name } ${ ua.getOS().version }`;
|
||||||
visitor.browser = `${ ua.getBrowser().name } ${ ua.getBrowser().version }`;
|
visitor.browser = `${ ua.getBrowser().name } ${ ua.getBrowser().version }`;
|
||||||
}
|
}
|
||||||
this.setState({ roomUser: visitor });
|
this.setState({ roomUser: visitor }, () => this.setHeader());
|
||||||
navigation.setParams({ roomUser: visitor });
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -195,14 +194,13 @@ class RoomInfoView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadRoom = async() => {
|
loadRoom = async() => {
|
||||||
const { navigation } = this.props;
|
const { route } = this.props;
|
||||||
let room = navigation.getParam('room');
|
let room = route.params?.room;
|
||||||
if (room && room.observe) {
|
if (room && room.observe) {
|
||||||
this.roomObservable = room.observe();
|
this.roomObservable = room.observe();
|
||||||
this.subscription = this.roomObservable
|
this.subscription = this.roomObservable
|
||||||
.subscribe((changes) => {
|
.subscribe((changes) => {
|
||||||
this.setState({ room: changes });
|
this.setState({ room: changes }, () => this.setHeader());
|
||||||
navigation.setParams({ room: changes });
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
@ -218,7 +216,7 @@ class RoomInfoView extends React.Component {
|
||||||
|
|
||||||
const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
|
const permissions = await RocketChat.hasPermission([PERMISSION_EDIT_ROOM], room.rid);
|
||||||
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
|
if (permissions[PERMISSION_EDIT_ROOM] && !room.prid) {
|
||||||
navigation.setParams({ showEdit: true });
|
this.setState({ showEdit: true }, () => this.setHeader());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,19 +234,8 @@ class RoomInfoView extends React.Component {
|
||||||
goRoom = () => {
|
goRoom = () => {
|
||||||
const { roomUser, room } = this.state;
|
const { roomUser, room } = this.state;
|
||||||
const { name, username } = roomUser;
|
const { name, username } = roomUser;
|
||||||
const { rooms, navigation, split } = this.props;
|
const { rooms, navigation, isMasterDetail } = this.props;
|
||||||
|
const params = {
|
||||||
if (room.rid) {
|
|
||||||
let navigate = navigation.push;
|
|
||||||
|
|
||||||
// if this is a room focused
|
|
||||||
if (rooms.includes(room.rid)) {
|
|
||||||
({ navigate } = navigation);
|
|
||||||
} else if (split) {
|
|
||||||
({ navigate } = Navigation);
|
|
||||||
}
|
|
||||||
|
|
||||||
navigate('RoomView', {
|
|
||||||
rid: room.rid,
|
rid: room.rid,
|
||||||
name: RocketChat.getRoomTitle({
|
name: RocketChat.getRoomTitle({
|
||||||
t: room.t,
|
t: room.t,
|
||||||
|
@ -257,7 +244,21 @@ class RoomInfoView extends React.Component {
|
||||||
}),
|
}),
|
||||||
t: room.t,
|
t: room.t,
|
||||||
roomUserId: RocketChat.getUidDirectMessage(room)
|
roomUserId: RocketChat.getUidDirectMessage(room)
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (room.rid) {
|
||||||
|
// if it's on master detail layout, we close the modal and replace RoomView
|
||||||
|
if (isMasterDetail) {
|
||||||
|
Navigation.navigate('DrawerNavigator');
|
||||||
|
goRoom({ item: params, isMasterDetail });
|
||||||
|
} else {
|
||||||
|
let navigate = navigation.push;
|
||||||
|
// if this is a room focused
|
||||||
|
if (rooms.includes(room.rid)) {
|
||||||
|
({ navigate } = navigation);
|
||||||
|
}
|
||||||
|
navigate('RoomView', params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,8 +328,8 @@ class RoomInfoView extends React.Component {
|
||||||
<ScrollView style={[styles.scroll, { backgroundColor: themes[theme].backgroundColor }]}>
|
<ScrollView style={[styles.scroll, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}
|
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||||
forceInset={{ vertical: 'never' }}
|
theme={theme}
|
||||||
testID='room-info-view'
|
testID='room-info-view'
|
||||||
>
|
>
|
||||||
<View style={[styles.avatarContainer, this.isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}>
|
<View style={[styles.avatarContainer, this.isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}>
|
||||||
|
@ -346,7 +347,8 @@ class RoomInfoView extends React.Component {
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
rooms: state.room.rooms
|
rooms: state.room.rooms,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withSplit(withTheme(RoomInfoView)));
|
export default connect(mapStateToProps)(withTheme(RoomInfoView));
|
||||||
|
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||||
import { FlatList, View } from 'react-native';
|
import { FlatList, View } from 'react-native';
|
||||||
import ActionSheet from 'react-native-action-sheet';
|
import ActionSheet from 'react-native-action-sheet';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { SafeAreaView } from 'react-navigation';
|
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
|
@ -22,30 +21,17 @@ import { CustomHeaderButtons, Item } from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { themedHeader } from '../../utils/navigation';
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
import { goRoom } from '../../utils/goRoom';
|
||||||
|
|
||||||
const PAGE_SIZE = 25;
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
class RoomMembersView extends React.Component {
|
class RoomMembersView extends React.Component {
|
||||||
static navigationOptions = ({ navigation, screenProps }) => {
|
|
||||||
const toggleStatus = navigation.getParam('toggleStatus', () => {});
|
|
||||||
const allUsers = navigation.getParam('allUsers');
|
|
||||||
const toggleText = allUsers ? I18n.t('Online') : I18n.t('All');
|
|
||||||
return {
|
|
||||||
title: I18n.t('Members'),
|
|
||||||
...themedHeader(screenProps.theme),
|
|
||||||
headerRight: (
|
|
||||||
<CustomHeaderButtons>
|
|
||||||
<Item title={toggleText} onPress={toggleStatus} testID='room-members-view-toggle-status' />
|
|
||||||
</CustomHeaderButtons>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
rid: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
members: PropTypes.array,
|
members: PropTypes.array,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
|
@ -54,7 +40,8 @@ class RoomMembersView extends React.Component {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
token: PropTypes.string
|
token: PropTypes.string
|
||||||
}),
|
}),
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
isMasterDetail: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -63,8 +50,8 @@ class RoomMembersView extends React.Component {
|
||||||
this.CANCEL_INDEX = 0;
|
this.CANCEL_INDEX = 0;
|
||||||
this.MUTE_INDEX = 1;
|
this.MUTE_INDEX = 1;
|
||||||
this.actionSheetOptions = [''];
|
this.actionSheetOptions = [''];
|
||||||
const { rid } = props.navigation.state.params;
|
const rid = props.route.params?.rid;
|
||||||
const room = props.navigation.getParam('room');
|
const room = props.route.params?.room;
|
||||||
this.state = {
|
this.state = {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
allUsers: false,
|
allUsers: false,
|
||||||
|
@ -87,15 +74,15 @@ class RoomMembersView extends React.Component {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
this.fetchMembers();
|
this.fetchMembers();
|
||||||
|
|
||||||
const { navigation } = this.props;
|
const { route } = this.props;
|
||||||
const { rid } = navigation.state.params;
|
const rid = route.params?.rid;
|
||||||
navigation.setParams({ toggleStatus: this.toggleStatus });
|
|
||||||
this.permissions = await RocketChat.hasPermission(['mute-user'], rid);
|
this.permissions = await RocketChat.hasPermission(['mute-user'], rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +92,20 @@ class RoomMembersView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHeader = () => {
|
||||||
|
const { allUsers } = this.state;
|
||||||
|
const { navigation } = this.props;
|
||||||
|
const toggleText = allUsers ? I18n.t('Online') : I18n.t('All');
|
||||||
|
navigation.setOptions({
|
||||||
|
title: I18n.t('Members'),
|
||||||
|
headerRight: () => (
|
||||||
|
<CustomHeaderButtons>
|
||||||
|
<Item title={toggleText} onPress={this.toggleStatus} testID='room-members-view-toggle-status' />
|
||||||
|
</CustomHeaderButtons>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onSearchChangeText = protectedFunction((text) => {
|
onSearchChangeText = protectedFunction((text) => {
|
||||||
const { members } = this.state;
|
const { members } = this.state;
|
||||||
let membersFiltered = [];
|
let membersFiltered = [];
|
||||||
|
@ -122,11 +123,11 @@ class RoomMembersView extends React.Component {
|
||||||
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
const query = await subsCollection.query(Q.where('name', item.username)).fetch();
|
||||||
if (query.length) {
|
if (query.length) {
|
||||||
const [room] = query;
|
const [room] = query;
|
||||||
this.goRoom({ rid: room.rid, name: item.username, room });
|
this.goRoom(room);
|
||||||
} else {
|
} else {
|
||||||
const result = await RocketChat.createDirectMessage(item.username);
|
const result = await RocketChat.createDirectMessage(item.username);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.goRoom({ rid: result.room._id, name: item.username });
|
this.goRoom({ rid: result.room?._id, name: item.username, t: 'd' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -180,7 +181,6 @@ class RoomMembersView extends React.Component {
|
||||||
const {
|
const {
|
||||||
rid, members, isLoading, allUsers, end
|
rid, members, isLoading, allUsers, end
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { navigation } = this.props;
|
|
||||||
if (isLoading || end) {
|
if (isLoading || end) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -194,19 +194,21 @@ class RoomMembersView extends React.Component {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
end: newMembers.length < PAGE_SIZE
|
end: newMembers.length < PAGE_SIZE
|
||||||
});
|
});
|
||||||
navigation.setParams({ allUsers });
|
this.setHeader();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
this.setState({ isLoading: false });
|
this.setState({ isLoading: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goRoom = async({ rid, name, room }) => {
|
goRoom = (item) => {
|
||||||
const { navigation } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
await navigation.popToTop();
|
if (isMasterDetail) {
|
||||||
navigation.navigate('RoomView', {
|
navigation.navigate('DrawerNavigator');
|
||||||
rid, name, t: 'd', room
|
} else {
|
||||||
});
|
navigation.popToTop();
|
||||||
|
}
|
||||||
|
goRoom({ item, isMasterDetail });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMute = async() => {
|
handleMute = async() => {
|
||||||
|
@ -261,7 +263,7 @@ class RoomMembersView extends React.Component {
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.list} testID='room-members-view' forceInset={{ vertical: 'never' }}>
|
<SafeAreaView testID='room-members-view' theme={theme}>
|
||||||
<StatusBar theme={theme} />
|
<StatusBar theme={theme} />
|
||||||
<FlatList
|
<FlatList
|
||||||
data={filtering ? membersFiltered : members}
|
data={filtering ? membersFiltered : members}
|
||||||
|
@ -289,7 +291,8 @@ class RoomMembersView extends React.Component {
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
user: getUserSelector(state)
|
user: getUserSelector(state),
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(RoomMembersView));
|
export default connect(mapStateToProps)(withTheme(RoomMembersView));
|
||||||
|
|
|
@ -18,7 +18,8 @@ const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginRight: isAndroid ? 15 : 5,
|
marginRight: isAndroid ? 15 : 5,
|
||||||
marginLeft: isAndroid ? androidMarginLeft : -10
|
marginLeft: isAndroid ? androidMarginLeft : -10,
|
||||||
|
justifyContent: 'center'
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -126,7 +127,7 @@ HeaderTitle.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = React.memo(({
|
const Header = React.memo(({
|
||||||
title, subtitle, type, status, usersTyping, width, height, prid, tmid, widthOffset, connecting, goRoomActionsView, roomUserId, theme
|
title, subtitle, type, status, usersTyping, width, height, prid, tmid, connecting, goRoomActionsView, roomUserId, theme
|
||||||
}) => {
|
}) => {
|
||||||
const portrait = height > width;
|
const portrait = height > width;
|
||||||
let scale = 1;
|
let scale = 1;
|
||||||
|
@ -143,7 +144,7 @@ const Header = React.memo(({
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
testID='room-view-header-actions'
|
testID='room-view-header-actions'
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[styles.container, { width: width - widthOffset }]}
|
style={styles.container}
|
||||||
disabled={tmid}
|
disabled={tmid}
|
||||||
>
|
>
|
||||||
<View style={[styles.titleContainer, tmid && styles.threadContainer]}>
|
<View style={[styles.titleContainer, tmid && styles.threadContainer]}>
|
||||||
|
@ -173,7 +174,6 @@ Header.propTypes = {
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
usersTyping: PropTypes.array,
|
usersTyping: PropTypes.array,
|
||||||
widthOffset: PropTypes.number,
|
|
||||||
connecting: PropTypes.bool,
|
connecting: PropTypes.bool,
|
||||||
roomUserId: PropTypes.string,
|
roomUserId: PropTypes.string,
|
||||||
goRoomActionsView: PropTypes.func
|
goRoomActionsView: PropTypes.func
|
||||||
|
|
|
@ -43,7 +43,7 @@ const Icon = React.memo(({
|
||||||
} else if (type === 'c') {
|
} else if (type === 'c') {
|
||||||
icon = 'hash';
|
icon = 'hash';
|
||||||
} else if (type === 'l') {
|
} else if (type === 'l') {
|
||||||
icon = 'livechat';
|
icon = 'omnichannel';
|
||||||
} else if (type === 'd') {
|
} else if (type === 'd') {
|
||||||
icon = 'team';
|
icon = 'team';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,6 +14,7 @@ class RightButtonsContainer extends React.PureComponent {
|
||||||
t: PropTypes.string,
|
t: PropTypes.string,
|
||||||
tmid: PropTypes.string,
|
tmid: PropTypes.string,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
isMasterDetail: PropTypes.bool,
|
||||||
toggleFollowThread: PropTypes.func
|
toggleFollowThread: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,9 +58,15 @@ class RightButtonsContainer extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
goThreadsView = () => {
|
goThreadsView = () => {
|
||||||
const { rid, t, navigation } = this.props;
|
const {
|
||||||
|
rid, t, navigation, isMasterDetail
|
||||||
|
} = this.props;
|
||||||
|
if (isMasterDetail) {
|
||||||
|
navigation.navigate('ModalStackNavigator', { screen: 'ThreadMessagesView', params: { rid, t } });
|
||||||
|
} else {
|
||||||
navigation.navigate('ThreadMessagesView', { rid, t });
|
navigation.navigate('ThreadMessagesView', { rid, t });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleFollowThread = () => {
|
toggleFollowThread = () => {
|
||||||
const { isFollowingThread } = this.state;
|
const { isFollowingThread } = this.state;
|
||||||
|
@ -104,7 +111,8 @@ class RightButtonsContainer extends React.PureComponent {
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
userId: getUserSelector(state).id,
|
userId: getUserSelector(state).id,
|
||||||
threadsEnabled: state.settings.Threads_enabled
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(RightButtonsContainer);
|
export default connect(mapStateToProps)(RightButtonsContainer);
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import { HeaderBackButton } from 'react-navigation-stack';
|
import { HeaderBackButton } from '@react-navigation/stack';
|
||||||
|
|
||||||
import { isIOS } from '../../../utils/deviceInfo';
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import Avatar from '../../../containers/Avatar';
|
import Avatar from '../../../containers/Avatar';
|
||||||
|
|
||||||
|
@ -14,19 +13,20 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const RoomHeaderLeft = ({
|
const RoomHeaderLeft = React.memo(({
|
||||||
tmid, unreadsCount, navigation, baseUrl, userId, token, title, t, theme, goRoomActionsView, split
|
tmid, unreadsCount, navigation, baseUrl, userId, token, title, t, theme, goRoomActionsView, isMasterDetail
|
||||||
}) => {
|
}) => {
|
||||||
if (!split || tmid) {
|
if (!isMasterDetail || tmid) {
|
||||||
|
const onPress = useCallback(() => navigation.goBack());
|
||||||
return (
|
return (
|
||||||
<HeaderBackButton
|
<HeaderBackButton
|
||||||
title={unreadsCount > 999 ? '+999' : unreadsCount || ' '}
|
label={unreadsCount > 999 ? '+999' : unreadsCount || ' '}
|
||||||
backTitleVisible={isIOS}
|
onPress={onPress}
|
||||||
onPress={() => navigation.goBack()}
|
|
||||||
tintColor={themes[theme].headerTintColor}
|
tintColor={themes[theme].headerTintColor}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const onPress = useCallback(() => goRoomActionsView(), []);
|
||||||
if (baseUrl && userId && token) {
|
if (baseUrl && userId && token) {
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<Avatar
|
||||||
|
@ -37,12 +37,12 @@ const RoomHeaderLeft = ({
|
||||||
style={styles.avatar}
|
style={styles.avatar}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
token={token}
|
token={token}
|
||||||
onPress={goRoomActionsView}
|
onPress={onPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
});
|
||||||
|
|
||||||
RoomHeaderLeft.propTypes = {
|
RoomHeaderLeft.propTypes = {
|
||||||
tmid: PropTypes.string,
|
tmid: PropTypes.string,
|
||||||
|
@ -55,7 +55,7 @@ RoomHeaderLeft.propTypes = {
|
||||||
t: PropTypes.string,
|
t: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
goRoomActionsView: PropTypes.func,
|
goRoomActionsView: PropTypes.func,
|
||||||
split: PropTypes.bool
|
isMasterDetail: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RoomHeaderLeft;
|
export default RoomHeaderLeft;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, InteractionManager, RefreshControl } from 'react-native';
|
import { FlatList, RefreshControl } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
@ -57,7 +57,7 @@ class List extends React.Component {
|
||||||
animated: false
|
animated: false
|
||||||
};
|
};
|
||||||
this.init();
|
this.init();
|
||||||
this.didFocusListener = props.navigation.addListener('didFocus', () => {
|
this.unsubscribeFocus = props.navigation.addListener('focus', () => {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ animated: true });
|
this.setState({ animated: true });
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,7 +106,6 @@ class List extends React.Component {
|
||||||
this.unsubscribeMessages();
|
this.unsubscribeMessages();
|
||||||
this.messagesSubscription = this.messagesObservable
|
this.messagesSubscription = this.messagesObservable
|
||||||
.subscribe((data) => {
|
.subscribe((data) => {
|
||||||
this.interaction = InteractionManager.runAfterInteractions(() => {
|
|
||||||
if (tmid && this.thread) {
|
if (tmid && this.thread) {
|
||||||
data = [this.thread, ...data];
|
data = [this.thread, ...data];
|
||||||
}
|
}
|
||||||
|
@ -117,7 +116,6 @@ class List extends React.Component {
|
||||||
this.state.messages = messages;
|
this.state.messages = messages;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,14 +155,11 @@ class List extends React.Component {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unsubscribeMessages();
|
this.unsubscribeMessages();
|
||||||
if (this.interaction && this.interaction.cancel) {
|
|
||||||
this.interaction.cancel();
|
|
||||||
}
|
|
||||||
if (this.onEndReached && this.onEndReached.stop) {
|
if (this.onEndReached && this.onEndReached.stop) {
|
||||||
this.onEndReached.stop();
|
this.onEndReached.stop();
|
||||||
}
|
}
|
||||||
if (this.didFocusListener && this.didFocusListener.remove) {
|
if (this.unsubscribeFocus) {
|
||||||
this.didFocusListener.remove();
|
this.unsubscribeFocus();
|
||||||
}
|
}
|
||||||
console.countReset(`${ this.constructor.name }.render calls`);
|
console.countReset(`${ this.constructor.name }.render calls`);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue