Merge branch 'develop' into appium-v2
This commit is contained in:
commit
4af0d7a881
|
@ -150,7 +150,7 @@ commands:
|
||||||
if [[ $CIRCLE_JOB == "android-build-official" ]]; then
|
if [[ $CIRCLE_JOB == "android-build-official" ]]; then
|
||||||
./gradlew bundleOfficialPlayRelease
|
./gradlew bundleOfficialPlayRelease
|
||||||
fi
|
fi
|
||||||
if [[ $CIRCLE_JOB == "android-build-experimental" ]]; then
|
if [[ $CIRCLE_JOB == "android-build-experimental" || "android-automatic-build-experimental" ]]; then
|
||||||
./gradlew bundleExperimentalPlayRelease
|
./gradlew bundleExperimentalPlayRelease
|
||||||
fi
|
fi
|
||||||
if [[ ! $KEYSTORE ]]; then
|
if [[ ! $KEYSTORE ]]; then
|
||||||
|
@ -169,7 +169,7 @@ commands:
|
||||||
--source-map=android/app/build/generated/sourcemaps/react/officialPlay/release/app.bundle.map \
|
--source-map=android/app/build/generated/sourcemaps/react/officialPlay/release/app.bundle.map \
|
||||||
--bundle android/app/build/generated/assets/react/officialPlay/release/app.bundle
|
--bundle android/app/build/generated/assets/react/officialPlay/release/app.bundle
|
||||||
fi
|
fi
|
||||||
if [[ $CIRCLE_JOB == "android-build-experimental" ]]; then
|
if [[ $CIRCLE_JOB == "android-build-experimental" || "android-automatic-build-experimental" ]]; then
|
||||||
npx bugsnag-source-maps upload-react-native \
|
npx bugsnag-source-maps upload-react-native \
|
||||||
--api-key=$BUGSNAG_KEY \
|
--api-key=$BUGSNAG_KEY \
|
||||||
--app-version-code=$CIRCLE_BUILD_NUM \
|
--app-version-code=$CIRCLE_BUILD_NUM \
|
||||||
|
@ -380,6 +380,18 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- android-build
|
- android-build
|
||||||
|
|
||||||
|
# Android automatic builds
|
||||||
|
android-automatic-build-experimental:
|
||||||
|
<<: *defaults
|
||||||
|
docker:
|
||||||
|
- image: circleci/android:api-29-node
|
||||||
|
environment:
|
||||||
|
<<: *android-env
|
||||||
|
<<: *bash-env
|
||||||
|
resource_class: large
|
||||||
|
steps:
|
||||||
|
- android-build
|
||||||
|
|
||||||
android-build-official:
|
android-build-official:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
docker:
|
docker:
|
||||||
|
@ -485,6 +497,10 @@ workflows:
|
||||||
type: approval
|
type: approval
|
||||||
requires:
|
requires:
|
||||||
- lint-testunit
|
- lint-testunit
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
ignore:
|
||||||
|
- develop
|
||||||
- android-build-experimental:
|
- android-build-experimental:
|
||||||
requires:
|
requires:
|
||||||
- android-hold-build-experimental
|
- android-hold-build-experimental
|
||||||
|
@ -521,3 +537,15 @@ workflows:
|
||||||
- android-google-play-beta-official:
|
- android-google-play-beta-official:
|
||||||
requires:
|
requires:
|
||||||
- android-hold-google-play-beta-official
|
- android-hold-google-play-beta-official
|
||||||
|
|
||||||
|
# Android Automatic Experimental
|
||||||
|
- android-automatic-build-experimental:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- develop
|
||||||
|
requires:
|
||||||
|
- lint-testunit
|
||||||
|
- android-google-play-production-experimental:
|
||||||
|
requires:
|
||||||
|
- android-automatic-build-experimental
|
||||||
|
|
|
@ -1,33 +1,21 @@
|
||||||
import { useBackHandler } from '@react-native-community/hooks';
|
import { useBackHandler } from '@react-native-community/hooks';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState, useCallback } from 'react';
|
||||||
import { Keyboard, Text } from 'react-native';
|
import { Keyboard } from 'react-native';
|
||||||
import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler';
|
import { Easing } from 'react-native-reanimated';
|
||||||
import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated';
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet';
|
import BottomSheet, { BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { useDimensions, useOrientation } from '../../dimensions';
|
import { useDimensions, useOrientation } from '../../dimensions';
|
||||||
import I18n from '../../i18n';
|
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||||
import * as List from '../List';
|
|
||||||
import { Button } from './Button';
|
|
||||||
import { Handle } from './Handle';
|
import { Handle } from './Handle';
|
||||||
import { IActionSheetItem, Item } from './Item';
|
import { TActionSheetOptions } from './Provider';
|
||||||
import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider';
|
import BottomSheetContent from './BottomSheetContent';
|
||||||
import styles, { ITEM_HEIGHT } from './styles';
|
import styles, { ITEM_HEIGHT } from './styles';
|
||||||
import { testProps } from '../../lib/methods/testProps';
|
|
||||||
|
|
||||||
const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({
|
|
||||||
length: ITEM_HEIGHT,
|
|
||||||
offset: ITEM_HEIGHT * index,
|
|
||||||
index
|
|
||||||
});
|
|
||||||
|
|
||||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
||||||
const MAX_SNAP_HEIGHT = 16;
|
const MIN_SNAP_HEIGHT = 16;
|
||||||
const CANCEL_HEIGHT = 64;
|
const CANCEL_HEIGHT = 64;
|
||||||
|
|
||||||
const ANIMATION_DURATION = 250;
|
const ANIMATION_DURATION = 250;
|
||||||
|
@ -40,27 +28,26 @@ const ANIMATION_CONFIG = {
|
||||||
|
|
||||||
const ActionSheet = React.memo(
|
const ActionSheet = React.memo(
|
||||||
forwardRef(({ children }: { children: React.ReactElement }, ref) => {
|
forwardRef(({ children }: { children: React.ReactElement }, ref) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const bottomSheetRef = useRef<ScrollBottomSheet<TActionSheetOptionsItem>>(null);
|
const bottomSheetRef = useRef<BottomSheet>(null);
|
||||||
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
|
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const { height } = useDimensions();
|
const { height } = useDimensions();
|
||||||
const { isLandscape } = useOrientation();
|
const { isLandscape } = useOrientation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
const maxSnap = Math.max(
|
const maxSnap = Math.min(
|
||||||
height -
|
|
||||||
// Items height
|
// Items height
|
||||||
ITEM_HEIGHT * (data?.options?.length || 0) -
|
ITEM_HEIGHT * (data?.options?.length || 0) +
|
||||||
// Handle height
|
// Handle height
|
||||||
HANDLE_HEIGHT -
|
HANDLE_HEIGHT +
|
||||||
// Custom header height
|
// Custom header height
|
||||||
(data?.headerHeight || 0) -
|
(data?.headerHeight || 0) +
|
||||||
// Insets bottom height (Notch devices)
|
// Insets bottom height (Notch devices)
|
||||||
insets.bottom -
|
insets.bottom +
|
||||||
// Cancel button height
|
// Cancel button height
|
||||||
(data?.hasCancel ? CANCEL_HEIGHT : 0),
|
(data?.hasCancel ? CANCEL_HEIGHT : 0),
|
||||||
MAX_SNAP_HEIGHT
|
height - MIN_SNAP_HEIGHT
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,14 +57,13 @@ const ActionSheet = React.memo(
|
||||||
* we'll provide more one snap
|
* we'll provide more one snap
|
||||||
* that point 50% of the whole screen
|
* that point 50% of the whole screen
|
||||||
*/
|
*/
|
||||||
const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height];
|
const snaps = maxSnap > height * 0.6 && !isLandscape && !data.snaps ? [height * 0.5, maxSnap] : [maxSnap];
|
||||||
const openedSnapIndex = snaps.length > 2 ? 1 : 0;
|
|
||||||
const closedSnapIndex = snaps.length - 1;
|
|
||||||
|
|
||||||
const toggleVisible = () => setVisible(!isVisible);
|
const toggleVisible = () => setVisible(!isVisible);
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
bottomSheetRef.current?.snapTo(closedSnapIndex);
|
bottomSheetRef.current?.close();
|
||||||
|
toggleVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
const show = (options: TActionSheetOptions) => {
|
const show = (options: TActionSheetOptions) => {
|
||||||
|
@ -85,12 +71,6 @@ const ActionSheet = React.memo(
|
||||||
toggleVisible();
|
toggleVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
|
|
||||||
if (nativeEvent.oldState === State.ACTIVE) {
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useBackHandler(() => {
|
useBackHandler(() => {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
hide();
|
hide();
|
||||||
|
@ -102,7 +82,6 @@ const ActionSheet = React.memo(
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
bottomSheetRef.current?.snapTo(openedSnapIndex);
|
|
||||||
}
|
}
|
||||||
}, [isVisible]);
|
}, [isVisible]);
|
||||||
|
|
||||||
|
@ -123,26 +102,18 @@ const ActionSheet = React.memo(
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderFooter = () =>
|
const renderBackdrop = useCallback(
|
||||||
data?.hasCancel ? (
|
props => (
|
||||||
<Button
|
<BottomSheetBackdrop
|
||||||
onPress={hide}
|
{...props}
|
||||||
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
appearsOnIndex={0}
|
||||||
// TODO: Remove when migrate Touch
|
// Backdrop should be visible all the time bottom sheet is open
|
||||||
theme={theme}
|
disappearsOnIndex={-1}
|
||||||
accessibilityLabel={I18n.t('Cancel')}>
|
opacity={colors.backdropOpacity}
|
||||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{I18n.t('Cancel')}</Text>
|
/>
|
||||||
</Button>
|
),
|
||||||
) : null;
|
[]
|
||||||
|
);
|
||||||
const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => <Item item={item} hide={hide} />;
|
|
||||||
|
|
||||||
const animatedPosition = React.useRef(new Value(0));
|
|
||||||
const opacity = interpolateNode(animatedPosition.current, {
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [0, themes[theme].backdropOpacity],
|
|
||||||
extrapolate: Extrapolate.CLAMP
|
|
||||||
}) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed.
|
|
||||||
|
|
||||||
const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {};
|
const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {};
|
||||||
|
|
||||||
|
@ -150,42 +121,19 @@ const ActionSheet = React.memo(
|
||||||
<>
|
<>
|
||||||
{children}
|
{children}
|
||||||
{isVisible && (
|
{isVisible && (
|
||||||
<>
|
<BottomSheet
|
||||||
<TapGestureHandler onHandlerStateChange={onBackdropPressed}>
|
|
||||||
<Animated.View
|
|
||||||
{...testProps('action-sheet-backdrop')}
|
|
||||||
style={[
|
|
||||||
styles.backdrop,
|
|
||||||
{
|
|
||||||
backgroundColor: themes[theme].backdropColor,
|
|
||||||
opacity
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</TapGestureHandler>
|
|
||||||
<ScrollBottomSheet<TActionSheetOptionsItem>
|
|
||||||
{...testProps('action-sheet')}
|
|
||||||
ref={bottomSheetRef}
|
ref={bottomSheetRef}
|
||||||
componentType='FlatList'
|
snapPoints={data?.snaps ? data.snaps : snaps}
|
||||||
snapPoints={snaps}
|
animationConfigs={ANIMATION_CONFIG}
|
||||||
initialSnapIndex={closedSnapIndex}
|
animateOnMount={true}
|
||||||
renderHandle={renderHandle}
|
backdropComponent={renderBackdrop}
|
||||||
onSettle={index => index === closedSnapIndex && toggleVisible()}
|
handleComponent={renderHandle}
|
||||||
animatedPosition={animatedPosition.current}
|
enablePanDownToClose
|
||||||
containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }}
|
style={{ ...styles.container, ...bottomSheet }}
|
||||||
animationConfig={ANIMATION_CONFIG}
|
backgroundStyle={{ backgroundColor: colors.focusedBackground }}
|
||||||
data={data.options}
|
onChange={index => index === -1 && toggleVisible()}>
|
||||||
renderItem={renderItem}
|
<BottomSheetContent options={data?.options} hide={hide} children={data?.children} hasCancel={data?.hasCancel} />
|
||||||
keyExtractor={item => item.title}
|
</BottomSheet>
|
||||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
|
||||||
contentContainerStyle={styles.content}
|
|
||||||
ItemSeparatorComponent={List.Separator}
|
|
||||||
ListHeaderComponent={List.Separator}
|
|
||||||
ListFooterComponent={renderFooter}
|
|
||||||
getItemLayout={getItemLayout}
|
|
||||||
removeClippedSubviews={isIOS}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Text } from 'react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import { BottomSheetView, BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
||||||
|
|
||||||
|
import { Button } from './Button';
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import { IActionSheetItem, Item } from './Item';
|
||||||
|
import { TActionSheetOptionsItem } from './Provider';
|
||||||
|
import styles from './styles';
|
||||||
|
import * as List from '../List';
|
||||||
|
|
||||||
|
interface IBottomSheetContentProps {
|
||||||
|
hasCancel?: boolean;
|
||||||
|
options?: TActionSheetOptionsItem[];
|
||||||
|
hide: () => void;
|
||||||
|
children?: React.ReactElement | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: IBottomSheetContentProps) => {
|
||||||
|
const { theme, colors } = useTheme();
|
||||||
|
|
||||||
|
const renderFooter = () =>
|
||||||
|
hasCancel ? (
|
||||||
|
<Button
|
||||||
|
onPress={hide}
|
||||||
|
style={[styles.button, { backgroundColor: colors.auxiliaryBackground }]}
|
||||||
|
// TODO: Remove when migrate Touch
|
||||||
|
theme={theme}
|
||||||
|
accessibilityLabel={I18n.t('Cancel')}>
|
||||||
|
<Text style={[styles.text, { color: colors.bodyText }]}>{I18n.t('Cancel')}</Text>
|
||||||
|
</Button>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => <Item item={item} hide={hide} />;
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
return (
|
||||||
|
<BottomSheetFlatList
|
||||||
|
data={options}
|
||||||
|
refreshing={false}
|
||||||
|
keyExtractor={item => item.title}
|
||||||
|
bounces={true}
|
||||||
|
renderItem={renderItem}
|
||||||
|
style={{ backgroundColor: colors.focusedBackground }}
|
||||||
|
keyboardDismissMode='interactive'
|
||||||
|
indicatorStyle='black'
|
||||||
|
contentContainerStyle={styles.content}
|
||||||
|
ItemSeparatorComponent={List.Separator}
|
||||||
|
ListHeaderComponent={List.Separator}
|
||||||
|
ListFooterComponent={renderFooter}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <BottomSheetView>{children}</BottomSheetView>;
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BottomSheetContent;
|
|
@ -13,10 +13,13 @@ export type TActionSheetOptionsItem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TActionSheetOptions = {
|
export type TActionSheetOptions = {
|
||||||
options: TActionSheetOptionsItem[];
|
options?: TActionSheetOptionsItem[];
|
||||||
headerHeight?: number;
|
headerHeight?: number;
|
||||||
customHeader?: React.ReactElement | null;
|
customHeader?: React.ReactElement | null;
|
||||||
hasCancel?: boolean;
|
hasCancel?: boolean;
|
||||||
|
type?: string;
|
||||||
|
children?: React.ReactElement | null;
|
||||||
|
snaps?: string[] | number[];
|
||||||
};
|
};
|
||||||
interface IActionSheetProvider {
|
interface IActionSheetProvider {
|
||||||
showActionSheet: (item: TActionSheetOptions) => void;
|
showActionSheet: (item: TActionSheetOptions) => void;
|
||||||
|
|
|
@ -46,8 +46,7 @@ export default StyleSheet.create({
|
||||||
},
|
},
|
||||||
bottomSheet: {
|
bottomSheet: {
|
||||||
width: '50%',
|
width: '50%',
|
||||||
alignSelf: 'center',
|
marginHorizontal: '25%'
|
||||||
left: '25%'
|
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
marginHorizontal: 16,
|
marginHorizontal: 16,
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
|
import Button from '.';
|
||||||
|
|
||||||
|
const buttonProps = {
|
||||||
|
title: 'Press me!',
|
||||||
|
type: 'primary',
|
||||||
|
onPress: () => {},
|
||||||
|
testID: 'testButton',
|
||||||
|
fontSize: 16,
|
||||||
|
style: {
|
||||||
|
padding: 10,
|
||||||
|
justifyContent: 'center'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stories = storiesOf('Button', module);
|
||||||
|
|
||||||
|
stories.add('primary button', () => <Button {...buttonProps} />);
|
||||||
|
|
||||||
|
stories.add('secondary button', () => <Button {...buttonProps} type='secondary' />);
|
||||||
|
|
||||||
|
stories.add('loading button', () => <Button loading {...buttonProps} />);
|
||||||
|
|
||||||
|
stories.add('disabled button', () => <Button disabled {...buttonProps} />);
|
||||||
|
|
||||||
|
stories.add('disabled loading button', () => <Button disabled loading {...buttonProps} />);
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { fireEvent, render } from '@testing-library/react-native';
|
||||||
|
|
||||||
|
import Button from '.';
|
||||||
|
|
||||||
|
const onPressMock = jest.fn();
|
||||||
|
|
||||||
|
const testProps = {
|
||||||
|
title: 'Press me!',
|
||||||
|
type: 'primary',
|
||||||
|
onPress: onPressMock,
|
||||||
|
testID: 'testButton',
|
||||||
|
initialText: 'Initial text',
|
||||||
|
textAfterPress: 'Button pressed!'
|
||||||
|
};
|
||||||
|
|
||||||
|
const TestButton = ({ loading = false, disabled = false }) => (
|
||||||
|
<View>
|
||||||
|
<Button
|
||||||
|
title={testProps.title}
|
||||||
|
type={testProps.title}
|
||||||
|
onPress={testProps.onPress}
|
||||||
|
testID={testProps.testID}
|
||||||
|
accessibilityLabel={testProps.title}
|
||||||
|
disabled={disabled}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('ButtonTests', () => {
|
||||||
|
test('rendered', async () => {
|
||||||
|
const { findByTestId } = render(<TestButton />);
|
||||||
|
const Button = await findByTestId(testProps.testID);
|
||||||
|
expect(Button).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rendered with correct title', async () => {
|
||||||
|
const { findByText } = render(<TestButton />);
|
||||||
|
const ButtonTitle = await findByText(testProps.title);
|
||||||
|
expect(ButtonTitle).toBeTruthy();
|
||||||
|
expect(ButtonTitle.props.children).toEqual(testProps.title);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('find button using accessibilityLabel', async () => {
|
||||||
|
const { findByA11yLabel } = render(<TestButton />);
|
||||||
|
const Button = await findByA11yLabel(testProps.title);
|
||||||
|
expect(Button).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('title not visible while loading', async () => {
|
||||||
|
const { queryByText } = render(<TestButton loading={true} />);
|
||||||
|
const ButtonTitle = await queryByText(testProps.title);
|
||||||
|
expect(ButtonTitle).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not trigger onPress on disabled button', async () => {
|
||||||
|
const { findByTestId } = render(<TestButton disabled={true} />);
|
||||||
|
const Button = await findByTestId(testProps.testID);
|
||||||
|
fireEvent.press(Button);
|
||||||
|
expect(onPressMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should trigger onPress function on button press', async () => {
|
||||||
|
const { findByTestId } = render(<TestButton />);
|
||||||
|
const Button = await findByTestId(testProps.testID);
|
||||||
|
fireEvent.press(Button);
|
||||||
|
expect(onPressMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots Button disabled button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"testButton\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3,\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots Button disabled loading button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"testButton\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"opacity\\":0.3,\\"padding\\":10}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#ffffff\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null]},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots Button loading button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"testButton\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"ActivityIndicator\\",\\"props\\":{\\"animating\\":true,\\"color\\":\\"#ffffff\\",\\"hidesWhenStopped\\":true,\\"size\\":\\"small\\",\\"style\\":[{\\"padding\\":16,\\"flex\\":1},null]},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots Button primary button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"testButton\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#1d74f5\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#ffffff\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots Button secondary button 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"accessibilityLabel\\":\\"testButton\\",\\"testID\\":\\"testButton\\",\\"focusable\\":true,\\"style\\":{\\"paddingHorizontal\\":14,\\"justifyContent\\":\\"center\\",\\"height\\":48,\\"borderRadius\\":2,\\"marginBottom\\":12,\\"backgroundColor\\":\\"#ffffff\\",\\"padding\\":10,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"textAlign\\":\\"center\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#2f343d\\",\\"fontSize\\":16},null],\\"accessibilityLabel\\":\\"Press me!\\"},\\"children\\":[\\"Press me!\\"]}]}"`;
|
|
@ -1,26 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ButtonProps, StyleSheet, Text } from 'react-native';
|
import { StyleProp, StyleSheet, Text, TextStyle } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable, { PlatformTouchableProps } from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { TSupportedThemes } from '../../theme';
|
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { testProps } from '../../lib/methods/testProps';
|
import { testProps } from '../../lib/methods/testProps';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
interface IButtonProps extends ButtonProps {
|
interface IButtonProps extends PlatformTouchableProps {
|
||||||
title: string;
|
title: string;
|
||||||
type: string;
|
onPress: () => void;
|
||||||
onPress(): void;
|
type?: string;
|
||||||
disabled: boolean;
|
backgroundColor?: string;
|
||||||
backgroundColor: string;
|
loading?: boolean;
|
||||||
loading: boolean;
|
color?: string;
|
||||||
theme: TSupportedThemes;
|
fontSize?: number;
|
||||||
color: string;
|
styleText?: StyleProp<TextStyle>[];
|
||||||
fontSize: any;
|
|
||||||
style: any;
|
|
||||||
styleText?: any;
|
|
||||||
testID: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -32,7 +27,6 @@ const styles = StyleSheet.create({
|
||||||
marginBottom: 12
|
marginBottom: 12
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: 16,
|
|
||||||
...sharedStyles.textMedium,
|
...sharedStyles.textMedium,
|
||||||
...sharedStyles.textAlignCenter
|
...sharedStyles.textAlignCenter
|
||||||
},
|
},
|
||||||
|
@ -41,34 +35,24 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class Button extends React.PureComponent<Partial<IButtonProps>, any> {
|
const Button = ({
|
||||||
static defaultProps = {
|
type = 'primary',
|
||||||
title: 'Press me!',
|
disabled = false,
|
||||||
type: 'primary',
|
loading = false,
|
||||||
onPress: () => alert('It works!'),
|
fontSize = 16,
|
||||||
disabled: false,
|
|
||||||
loading: false
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
title,
|
title,
|
||||||
type,
|
|
||||||
onPress,
|
onPress,
|
||||||
disabled,
|
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
color,
|
color,
|
||||||
loading,
|
|
||||||
style,
|
style,
|
||||||
theme,
|
|
||||||
fontSize,
|
|
||||||
styleText,
|
styleText,
|
||||||
testID,
|
testID,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
}: IButtonProps): React.ReactElement => {
|
||||||
|
const { colors } = useTheme();
|
||||||
const isPrimary = type === 'primary';
|
const isPrimary = type === 'primary';
|
||||||
|
|
||||||
let textColor = isPrimary ? themes[theme!].buttonText : themes[theme!].bodyText;
|
let textColor = isPrimary ? colors.buttonText : colors.bodyText;
|
||||||
if (color) {
|
if (color) {
|
||||||
textColor = color;
|
textColor = color;
|
||||||
}
|
}
|
||||||
|
@ -79,22 +63,22 @@ export default class Button extends React.PureComponent<Partial<IButtonProps>, a
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
backgroundColor
|
backgroundColor ? { backgroundColor } : { backgroundColor: isPrimary ? colors.actionTintColor : colors.backgroundColor },
|
||||||
? { backgroundColor }
|
|
||||||
: { backgroundColor: isPrimary ? themes[theme!].actionTintColor : themes[theme!].backgroundColor },
|
|
||||||
disabled && styles.disabled,
|
disabled && styles.disabled,
|
||||||
style
|
style
|
||||||
]}
|
]}
|
||||||
|
accessibilityLabel={title}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
{...testProps((testID || title) as string)}>
|
{...testProps((testID || title) as string)}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<ActivityIndicator color={textColor} />
|
<ActivityIndicator color={textColor} />
|
||||||
) : (
|
) : (
|
||||||
<Text style={[styles.text, { color: textColor }, fontSize && { fontSize }, styleText]} accessibilityLabel={title}>
|
<Text style={[styles.text, { color: textColor, fontSize }, styleText]} accessibilityLabel={title}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
export default Button;
|
||||||
|
|
|
@ -327,7 +327,6 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, ILoginServi
|
||||||
title={collapsed ? I18n.t('Onboarding_more_options') : I18n.t('Onboarding_less_options')}
|
title={collapsed ? I18n.t('Onboarding_more_options') : I18n.t('Onboarding_less_options')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
onPress={this.toggleServices}
|
onPress={this.toggleServices}
|
||||||
theme={theme}
|
|
||||||
style={styles.options}
|
style={styles.options}
|
||||||
color={themes[theme].actionTintColor}
|
color={themes[theme].actionTintColor}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Q } from '@nozbe/watermelondb';
|
||||||
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
|
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';
|
||||||
|
|
||||||
import { generateTriggerId } from '../../lib/methods/actions';
|
import { generateTriggerId } from '../../lib/methods/actions';
|
||||||
import TextInput, { IThemedTextInput } from '../../presentation/TextInput';
|
import TextInput, { IThemedTextInput } from '../TextInput';
|
||||||
import { userTyping as userTypingAction } from '../../actions/room';
|
import { userTyping as userTypingAction } from '../../actions/room';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
|
@ -678,7 +678,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
canUploadFile = (file: any) => {
|
canUploadFile = (file: any) => {
|
||||||
const { permissionToUpload } = this.state;
|
const { permissionToUpload } = this.state;
|
||||||
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
const { FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize } = this.props;
|
||||||
const result = canUploadFile(file, FileUpload_MediaTypeWhiteList, FileUpload_MaxFileSize, permissionToUpload);
|
const result = canUploadFile({
|
||||||
|
file,
|
||||||
|
allowList: FileUpload_MediaTypeWhiteList,
|
||||||
|
maxFileSize: FileUpload_MaxFileSize,
|
||||||
|
permissionToUploadFile: permissionToUpload
|
||||||
|
});
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -726,7 +731,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
chooseFile = async () => {
|
chooseFile = async () => {
|
||||||
logEvent(events.ROOM_BOX_ACTION_FILE);
|
logEvent(events.ROOM_BOX_ACTION_FILE);
|
||||||
try {
|
try {
|
||||||
const res = await DocumentPicker.pick({
|
const res = await DocumentPicker.pickSingle({
|
||||||
type: [DocumentPicker.types.allFiles]
|
type: [DocumentPicker.types.allFiles]
|
||||||
});
|
});
|
||||||
const file = {
|
const file = {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleProp, ViewStyle } from 'react-native';
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
import { SvgUri } from 'react-native-svg';
|
import { SvgUri } from 'react-native-svg';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { OmnichannelSourceType, IApplicationState, IOmnichannelSource } from '../../definitions';
|
import { OmnichannelSourceType, IOmnichannelSource } from '../../definitions';
|
||||||
import { STATUS_COLORS } from '../../lib/constants';
|
import { STATUS_COLORS } from '../../lib/constants';
|
||||||
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
import { CustomIcon, TIconsName } from '../CustomIcon';
|
import { CustomIcon, TIconsName } from '../CustomIcon';
|
||||||
|
|
||||||
interface IIconMap {
|
interface IIconMap {
|
||||||
|
@ -29,8 +29,8 @@ interface IOmnichannelRoomIconProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OmnichannelRoomIcon = ({ size, style, sourceType, status }: IOmnichannelRoomIconProps) => {
|
export const OmnichannelRoomIcon = ({ size, style, sourceType, status }: IOmnichannelRoomIconProps) => {
|
||||||
const baseUrl = useSelector((state: IApplicationState) => state.server?.server);
|
const baseUrl = useAppSelector(state => state.server?.server);
|
||||||
const connected = useSelector((state: IApplicationState) => state.meteor?.connected);
|
const connected = useAppSelector(state => state.meteor?.connected);
|
||||||
|
|
||||||
if (sourceType?.type === OmnichannelSourceType.APP && sourceType.id && sourceType.sidebarIcon && connected) {
|
if (sourceType?.type === OmnichannelSourceType.APP && sourceType.id && sourceType.sidebarIcon && connected) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Touchable from 'react-native-platform-touchable';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { CustomIcon } from './CustomIcon';
|
import { CustomIcon } from './CustomIcon';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from './TextInput';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import I18n from '../i18n';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from './TextInput';
|
||||||
import { isIOS, isTablet } from '../utils/deviceInfo';
|
import { isIOS, isTablet } from '../utils/deviceInfo';
|
||||||
import { useOrientation } from '../dimensions';
|
import { useOrientation } from '../dimensions';
|
||||||
import { testProps } from '../lib/methods/testProps';
|
import { testProps } from '../lib/methods/testProps';
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { IApplicationState, TUserStatus } from '../../definitions';
|
import { TUserStatus } from '../../definitions';
|
||||||
import Status from './Status';
|
import Status from './Status';
|
||||||
import { IStatus } from './definition';
|
import { IStatus } from './definition';
|
||||||
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
|
||||||
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
||||||
const status = useSelector((state: IApplicationState) =>
|
const status = useAppSelector(state =>
|
||||||
state.meteor.connected ? state.activeUsers[id] && state.activeUsers[id].status : 'loading'
|
state.meteor.connected ? state.activeUsers[id] && state.activeUsers[id].status : 'loading'
|
||||||
) as TUserStatus;
|
) as TUserStatus;
|
||||||
return <Status size={size} style={style} status={status} {...props} />;
|
return <Status size={size} style={style} status={status} {...props} />;
|
||||||
|
|
|
@ -2,13 +2,13 @@ import React from 'react';
|
||||||
import { StyleProp, StyleSheet, Text, TextInputProps, TextInput as RNTextInput, TextStyle, View, ViewStyle } from 'react-native';
|
import { StyleProp, StyleSheet, Text, TextInputProps, TextInput as RNTextInput, TextStyle, View, ViewStyle } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from './index';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { CustomIcon, TIconsName } from './CustomIcon';
|
import { CustomIcon, TIconsName } from '../CustomIcon';
|
||||||
import ActivityIndicator from './ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
import { testProps } from '../lib/methods/testProps';
|
import { TSupportedThemes } from '../../theme';
|
||||||
import { TSupportedThemes } from '../theme';
|
import { testProps } from '../../lib/methods/testProps';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
error: {
|
error: {
|
||||||
|
@ -71,7 +71,7 @@ interface IRCTextInputState {
|
||||||
showPassword: boolean;
|
showPassword: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class RCTextInput extends React.PureComponent<IRCTextInputProps, IRCTextInputState> {
|
export default class FormTextInput extends React.PureComponent<IRCTextInputProps, IRCTextInputState> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
error: {},
|
error: {},
|
||||||
theme: 'light'
|
theme: 'light'
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
import { View, StyleSheet } from 'react-native';
|
import { View, StyleSheet } from 'react-native';
|
||||||
import TextInput from './TextInput';
|
import FormTextInput from './FormTextInput';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
paddingHorizontal: {
|
paddingHorizontal: {
|
||||||
|
@ -23,9 +23,9 @@ const theme = 'light';
|
||||||
stories.add('Short and Long Text', () => (
|
stories.add('Short and Long Text', () => (
|
||||||
<>
|
<>
|
||||||
<View style={styles.paddingHorizontal}>
|
<View style={styles.paddingHorizontal}>
|
||||||
<TextInput label='Short Text' placeholder='placeholder' value={item.name} theme={theme} />
|
<FormTextInput label='Short Text' placeholder='placeholder' value={item.name} theme={theme} />
|
||||||
|
|
||||||
<TextInput label='Long Text' placeholder='placeholder' value={item.longText} theme={theme} />
|
<FormTextInput label='Long Text' placeholder='placeholder' value={item.longText} theme={theme} />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
));
|
));
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { I18nManager, StyleProp, StyleSheet, TextInput, TextStyle } from 'react-native';
|
import { I18nManager, StyleProp, StyleSheet, TextInput, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { IRCTextInputProps } from '../containers/TextInput';
|
import { IRCTextInputProps } from './FormTextInput';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { testProps } from '../lib/methods/testProps';
|
import { TSupportedThemes } from '../../theme';
|
||||||
import { TSupportedThemes } from '../theme';
|
import { testProps } from '../../lib/methods/testProps';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
input: {
|
input: {
|
|
@ -6,7 +6,7 @@ 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 { connect } from 'react-redux';
|
||||||
|
|
||||||
import TextInput from '../TextInput';
|
import FormTextInput from '../TextInput/FormTextInput';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
@ -114,7 +114,7 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) =
|
||||||
]}>
|
]}>
|
||||||
<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
|
<FormTextInput
|
||||||
value={code}
|
value={code}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
|
inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
|
||||||
|
@ -139,16 +139,8 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) =
|
||||||
backgroundColor={themes[theme].chatComponentBackground}
|
backgroundColor={themes[theme].chatComponentBackground}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
onPress={onCancel}
|
onPress={onCancel}
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
title={I18n.t('Send')}
|
|
||||||
type='primary'
|
|
||||||
style={styles.button}
|
|
||||||
onPress={onSubmit}
|
|
||||||
theme={theme}
|
|
||||||
testID='two-factor-send'
|
|
||||||
/>
|
/>
|
||||||
|
<Button title={I18n.t('Send')} type='primary' style={styles.button} onPress={onSubmit} testID='two-factor-send' />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -4,10 +4,8 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { IActions } from './interfaces';
|
import { IActions } from './interfaces';
|
||||||
import { useTheme } from '../../theme';
|
|
||||||
|
|
||||||
export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
|
export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
|
const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
|
||||||
const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
|
const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
|
||||||
|
|
||||||
|
@ -18,7 +16,7 @@ export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Elements />
|
<Elements />
|
||||||
{showMoreVisible && <Button theme={theme} title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
|
{showMoreVisible && <Button title={I18n.t('Show_more')} onPress={() => setShowMoreVisible(false)} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -56,9 +56,7 @@ export const DatePicker = ({ element, language, action, context, loading, value,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let button = placeholder ? (
|
let button = placeholder ? <Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} /> : null;
|
||||||
<Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} theme={theme} />
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
if (context === BLOCK_CONTEXT.FORM) {
|
if (context === BLOCK_CONTEXT.FORM) {
|
||||||
button = (
|
button = (
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||||
|
|
||||||
import Button from '../../Button';
|
import Button from '../../Button';
|
||||||
import TextInput from '../../TextInput';
|
import FormTextInput from '../../TextInput/FormTextInput';
|
||||||
import { textParser } from '../utils';
|
import { textParser } from '../utils';
|
||||||
import { themes } from '../../../lib/constants';
|
import { themes } from '../../../lib/constants';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
|
@ -139,7 +139,7 @@ export const MultiSelect = React.memo(
|
||||||
return (
|
return (
|
||||||
<View style={[styles.modal, { backgroundColor: themes[theme].backgroundColor }]}>
|
<View style={[styles.modal, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
<View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}>
|
<View style={[styles.content, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
testID='multi-select-search'
|
testID='multi-select-search'
|
||||||
onChangeText={onSearch || onSearchChange}
|
onChangeText={onSearch || onSearchChange}
|
||||||
placeholder={I18n.t('Search')}
|
placeholder={I18n.t('Search')}
|
||||||
|
@ -157,7 +157,7 @@ export const MultiSelect = React.memo(
|
||||||
});
|
});
|
||||||
|
|
||||||
let button = multiselect ? (
|
let button = multiselect ? (
|
||||||
<Button title={`${selected.length} selecteds`} onPress={onShow} loading={loading} theme={theme} />
|
<Button title={`${selected.length} selecteds`} onPress={onShow} loading={loading} />
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
onPress={onShow}
|
onPress={onShow}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKi
|
||||||
|
|
||||||
import Markdown, { MarkdownPreview } from '../markdown';
|
import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import TextInput from '../TextInput';
|
import FormTextInput from '../TextInput/FormTextInput';
|
||||||
import { textParser, useBlockContext } from './utils';
|
import { textParser, useBlockContext } from './utils';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
@ -63,7 +63,6 @@ class MessageParser extends UiKitParserMessage {
|
||||||
button(element: IButton, context: BlockContext) {
|
button(element: IButton, context: BlockContext) {
|
||||||
const { text, value, actionId, style } = element;
|
const { text, value, actionId, style } = element;
|
||||||
const [{ loading }, action] = useBlockContext(element, context);
|
const [{ loading }, action] = useBlockContext(element, context);
|
||||||
const { theme } = useContext(ThemeContext);
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
key={actionId}
|
key={actionId}
|
||||||
|
@ -72,7 +71,6 @@ class MessageParser extends UiKitParserMessage {
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onPress={() => action({ value })}
|
onPress={() => action({ value })}
|
||||||
style={styles.button}
|
style={styles.button}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +169,7 @@ class ModalParser extends UiKitParserModal {
|
||||||
const { theme } = useContext(ThemeContext);
|
const { theme } = useContext(ThemeContext);
|
||||||
const { multiline, actionId, placeholder } = element;
|
const { multiline, actionId, placeholder } = element;
|
||||||
return (
|
return (
|
||||||
<TextInput
|
<FormTextInput
|
||||||
key={actionId}
|
key={actionId}
|
||||||
placeholder={plainText(placeholder)}
|
placeholder={plainText(placeholder)}
|
||||||
multiline={multiline}
|
multiline={multiline}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { Node, Parser } from 'commonmark';
|
import { Parser } from 'commonmark';
|
||||||
import Renderer from 'commonmark-react-renderer';
|
import Renderer from 'commonmark-react-renderer';
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
|
||||||
import MarkdownLink from './Link';
|
import MarkdownLink from './Link';
|
||||||
import MarkdownList from './List';
|
import MarkdownList from './List';
|
||||||
import MarkdownListItem from './ListItem';
|
import MarkdownListItem from './ListItem';
|
||||||
|
@ -37,7 +36,6 @@ interface IMarkdownProps {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
tmid?: string;
|
tmid?: string;
|
||||||
isEdited?: boolean;
|
|
||||||
numberOfLines?: number;
|
numberOfLines?: number;
|
||||||
customEmojis?: boolean;
|
customEmojis?: boolean;
|
||||||
useRealName?: boolean;
|
useRealName?: boolean;
|
||||||
|
@ -133,9 +131,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
|
|
||||||
table: this.renderTable,
|
table: this.renderTable,
|
||||||
table_row: this.renderTableRow,
|
table_row: this.renderTableRow,
|
||||||
table_cell: this.renderTableCell,
|
table_cell: this.renderTableCell
|
||||||
|
|
||||||
editedIndicator: this.renderEditedIndicator
|
|
||||||
},
|
},
|
||||||
renderParagraphsInLists: true
|
renderParagraphsInLists: true
|
||||||
});
|
});
|
||||||
|
@ -145,21 +141,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
return !!enableMessageParser && !!md;
|
return !!enableMessageParser && !!md;
|
||||||
}
|
}
|
||||||
|
|
||||||
editedMessage = (ast: any) => {
|
|
||||||
const { isEdited } = this.props;
|
|
||||||
if (isEdited) {
|
|
||||||
const editIndicatorNode = new Node('edited_indicator');
|
|
||||||
if (ast.lastChild && ['heading', 'paragraph'].includes(ast.lastChild.type)) {
|
|
||||||
ast.lastChild.appendChild(editIndicatorNode);
|
|
||||||
} else {
|
|
||||||
const node = new Node('paragraph');
|
|
||||||
node.appendChild(editIndicatorNode);
|
|
||||||
|
|
||||||
ast.appendChild(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderText = ({ context, literal }: { context: []; literal: string }) => {
|
renderText = ({ context, literal }: { context: []; literal: string }) => {
|
||||||
const { numberOfLines, style = [] } = this.props;
|
const { numberOfLines, style = [] } = this.props;
|
||||||
const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])];
|
const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])];
|
||||||
|
@ -274,11 +255,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} />;
|
return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderEditedIndicator = () => {
|
|
||||||
const { theme } = this.props;
|
|
||||||
return <Text style={[styles.edited, { color: themes[theme].auxiliaryText }]}> ({I18n.t('edited')})</Text>;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderHeading = ({ children, level }: any) => {
|
renderHeading = ({ children, level }: any) => {
|
||||||
const { numberOfLines, theme } = this.props;
|
const { numberOfLines, theme } = this.props;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -373,7 +349,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
let ast = parser.parse(m);
|
let ast = parser.parse(m);
|
||||||
ast = mergeTextNodes(ast);
|
ast = mergeTextNodes(ast);
|
||||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
||||||
this.editedMessage(ast);
|
|
||||||
return this.renderer.render(ast);
|
return this.renderer.render(ast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,6 @@ export default StyleSheet.create({
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
},
|
},
|
||||||
edited: {
|
|
||||||
fontSize: 14,
|
|
||||||
...sharedStyles.textRegular
|
|
||||||
},
|
|
||||||
heading1: {
|
heading1: {
|
||||||
...sharedStyles.textBold,
|
...sharedStyles.textBold,
|
||||||
fontSize: 24
|
fontSize: 24
|
||||||
|
|
|
@ -43,7 +43,7 @@ const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (element.type === 'button') {
|
if (element.type === 'button') {
|
||||||
return <Button theme={theme} onPress={onPress} title={element.text} />;
|
return <Button onPress={onPress} title={element.text} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -59,7 +59,6 @@ const Content = React.memo(
|
||||||
getCustomEmoji={props.getCustomEmoji}
|
getCustomEmoji={props.getCustomEmoji}
|
||||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
isEdited={props.isEdited}
|
|
||||||
channels={props.channels}
|
channels={props.channels}
|
||||||
mentions={props.mentions}
|
mentions={props.mentions}
|
||||||
navToRoomInfo={props.navToRoomInfo}
|
navToRoomInfo={props.navToRoomInfo}
|
||||||
|
@ -72,7 +71,7 @@ const Content = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a encrypted message and is not a preview
|
// If this is a encrypted message and is not a preview
|
||||||
if (props.type === E2E_MESSAGE_TYPE && !isPreview) {
|
if (props.type === E2E_MESSAGE_TYPE && !isPreview && !props.isHeader) {
|
||||||
content = (
|
content = (
|
||||||
<View style={styles.flex}>
|
<View style={styles.flex}>
|
||||||
<View style={styles.contentContainer}>{content}</View>
|
<View style={styles.contentContainer}>{content}</View>
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
|
||||||
|
import { CustomIcon } from '../CustomIcon';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import { themes } from '../../lib/constants';
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
|
const Edited = memo(({ isEdited }: { isEdited: boolean }) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
if (!isEdited) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.leftIcons}>
|
||||||
|
<CustomIcon name='edit' size={16} color={themes[theme].auxiliaryText} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Edited;
|
|
@ -17,7 +17,7 @@ const Encrypted = React.memo(({ type }: { type: string }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touchable onPress={onEncryptedPress} style={styles.encrypted} hitSlop={BUTTON_HIT_SLOP}>
|
<Touchable onPress={onEncryptedPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
|
||||||
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />
|
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,6 +20,8 @@ import CallButton from './CallButton';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
import Edited from './Edited';
|
||||||
|
import MessageError from './MessageError';
|
||||||
|
|
||||||
const MessageInner = React.memo((props: IMessageInner) => {
|
const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
const { attachments } = props;
|
const { attachments } = props;
|
||||||
|
@ -102,6 +104,12 @@ const Message = React.memo((props: IMessage) => {
|
||||||
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
|
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
|
||||||
<MessageInner {...props} />
|
<MessageInner {...props} />
|
||||||
</View>
|
</View>
|
||||||
|
{!props.isHeader ? (
|
||||||
|
<>
|
||||||
|
<Edited isEdited={props.isEdited} />
|
||||||
|
<MessageError hasError={props.hasError} />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
|
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,8 +18,8 @@ const MessageError = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
|
<Touchable onPress={onErrorPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
|
||||||
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} />
|
<CustomIcon name='warning' color={themes[theme].dangerColor} size={16} />
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useTheme } from '../../theme';
|
||||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
if (isReadReceiptEnabled && !unread && unread !== null) {
|
if (isReadReceiptEnabled && !unread && unread !== null) {
|
||||||
return <CustomIcon name='check' color={themes[theme].tintColor} size={15} style={styles.readReceipt} />;
|
return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={[styles.leftIcons, styles.readReceipt]} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,12 +11,12 @@ import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
||||||
import { IAttachment } from '../../definitions/IAttachment';
|
import { IAttachment, TGetCustomEmoji } from '../../definitions';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
import Attachments from './Attachments';
|
import Attachments from './Attachments';
|
||||||
import { TSupportedThemes, useTheme } from '../../theme';
|
import { TSupportedThemes, useTheme } from '../../theme';
|
||||||
import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl';
|
import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl';
|
||||||
|
import messageStyles from './styles';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
|
@ -44,15 +44,9 @@ const styles = StyleSheet.create({
|
||||||
marginBottom: 8
|
marginBottom: 8
|
||||||
},
|
},
|
||||||
author: {
|
author: {
|
||||||
flex: 1,
|
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium
|
||||||
},
|
},
|
||||||
time: {
|
|
||||||
fontSize: 12,
|
|
||||||
marginLeft: 8,
|
|
||||||
...sharedStyles.textRegular
|
|
||||||
},
|
|
||||||
fieldsContainer: {
|
fieldsContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
|
@ -106,8 +100,8 @@ const Title = React.memo(
|
||||||
{attachment.author_name ? (
|
{attachment.author_name ? (
|
||||||
<Text style={[styles.author, { color: themes[theme].auxiliaryTintColor }]}>{attachment.author_name}</Text>
|
<Text style={[styles.author, { color: themes[theme].auxiliaryTintColor }]}>{attachment.author_name}</Text>
|
||||||
) : null}
|
) : null}
|
||||||
|
{time ? <Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text> : null}
|
||||||
{attachment.title ? <Text style={[styles.title, { color: themes[theme].bodyText }]}>{attachment.title}</Text> : null}
|
{attachment.title ? <Text style={[styles.title, { color: themes[theme].bodyText }]}>{attachment.title}</Text> : null}
|
||||||
{time ? <Text style={[styles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text> : null}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import MessageContext from './Context';
|
||||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
||||||
import { SubscriptionType } from '../../definitions';
|
import { SubscriptionType } from '../../definitions';
|
||||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||||
|
import Edited from './Edited';
|
||||||
|
import Encrypted from './Encrypted';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -19,7 +21,12 @@ const styles = StyleSheet.create({
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
|
actionIcons: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
username: {
|
username: {
|
||||||
|
flexShrink: 1,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
lineHeight: 22,
|
lineHeight: 22,
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium
|
||||||
|
@ -29,7 +36,6 @@ const styles = StyleSheet.create({
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
flexShrink: 1,
|
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
|
@ -41,7 +47,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IMessageUser {
|
interface IMessageUser {
|
||||||
isHeader?: boolean;
|
isHeader?: boolean;
|
||||||
hasError?: boolean;
|
hasError: boolean;
|
||||||
useRealName?: boolean;
|
useRealName?: boolean;
|
||||||
author?: {
|
author?: {
|
||||||
_id: string;
|
_id: string;
|
||||||
|
@ -53,10 +59,11 @@ interface IMessageUser {
|
||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
||||||
type: string;
|
type: string;
|
||||||
|
isEdited: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const User = React.memo(
|
const User = React.memo(
|
||||||
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => {
|
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, isEdited, ...props }: IMessageUser) => {
|
||||||
const { user } = useContext(MessageContext);
|
const { user } = useContext(MessageContext);
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
@ -99,9 +106,13 @@ const User = React.memo(
|
||||||
<Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}>
|
<Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}>
|
||||||
{textContent}
|
{textContent}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
|
<View style={styles.actionIcons}>
|
||||||
{hasError ? <MessageError hasError={hasError} {...props} /> : null}
|
<Encrypted type={type} />
|
||||||
|
<Edited isEdited={isEdited} />
|
||||||
|
<MessageError hasError={hasError} {...props} />
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
|
||||||
|
|
||||||
get isInfo(): string | boolean {
|
get isInfo(): string | boolean {
|
||||||
const { item } = this.props;
|
const { item } = this.props;
|
||||||
if (['e2e', 'discussion-created'].includes(item.t)) {
|
if (['e2e', 'discussion-created', 'jitsi_call_started'].includes(item.t)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return item.t;
|
return item.t;
|
||||||
|
|
|
@ -60,6 +60,8 @@ export interface IMessageContent {
|
||||||
isIgnored: boolean;
|
isIgnored: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
|
hasError: boolean;
|
||||||
|
isHeader: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessageEmoji {
|
export interface IMessageEmoji {
|
||||||
|
|
|
@ -74,10 +74,6 @@ export default StyleSheet.create({
|
||||||
avatarSmall: {
|
avatarSmall: {
|
||||||
marginLeft: 16
|
marginLeft: 16
|
||||||
},
|
},
|
||||||
errorButton: {
|
|
||||||
paddingLeft: 10,
|
|
||||||
paddingVertical: 5
|
|
||||||
},
|
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
marginTop: 8,
|
marginTop: 8,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -133,7 +129,7 @@ export default StyleSheet.create({
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
fontSize: 12,
|
fontSize: 13,
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
},
|
},
|
||||||
|
@ -167,12 +163,13 @@ export default StyleSheet.create({
|
||||||
threadBell: {
|
threadBell: {
|
||||||
marginLeft: 8
|
marginLeft: 8
|
||||||
},
|
},
|
||||||
|
leftIcons: {
|
||||||
|
paddingLeft: 5,
|
||||||
|
paddingVertical: 5
|
||||||
|
},
|
||||||
readReceipt: {
|
readReceipt: {
|
||||||
lineHeight: 20
|
lineHeight: 20
|
||||||
},
|
},
|
||||||
encrypted: {
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
threadDetails: {
|
threadDetails: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
marginLeft: 12
|
marginLeft: 12
|
||||||
|
|
|
@ -56,3 +56,14 @@ export interface IServerAttachment {
|
||||||
url: string;
|
url: string;
|
||||||
user: Pick<IUser, '_id' | 'username' | 'name'>;
|
user: Pick<IUser, '_id' | 'username' | 'name'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IShareAttachment {
|
||||||
|
filename: string;
|
||||||
|
description?: string;
|
||||||
|
size: number;
|
||||||
|
mime?: string;
|
||||||
|
path: string;
|
||||||
|
canUpload: boolean;
|
||||||
|
error?: any;
|
||||||
|
uri: string;
|
||||||
|
}
|
||||||
|
|
|
@ -18,4 +18,7 @@ export interface ICredentials {
|
||||||
fullName?: AppleAuthenticationFullName | null;
|
fullName?: AppleAuthenticationFullName | null;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
identityToken?: string | null;
|
identityToken?: string | null;
|
||||||
|
credentialToken?: string;
|
||||||
|
saml?: boolean;
|
||||||
|
cas?: { credentialToken?: string };
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface IVisitorEmail {
|
||||||
|
|
||||||
export interface ILivechatVisitor extends IRocketChatRecord {
|
export interface ILivechatVisitor extends IRocketChatRecord {
|
||||||
username: string;
|
username: string;
|
||||||
ts: Date;
|
ts: Date | string;
|
||||||
token: string;
|
token: string;
|
||||||
department?: string;
|
department?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
|
@ -89,11 +89,7 @@ export interface IMessageFromServer {
|
||||||
drid?: string;
|
drid?: string;
|
||||||
dcount?: number;
|
dcount?: number;
|
||||||
dml: string | Date;
|
dml: string | Date;
|
||||||
starred?:
|
starred?: boolean;
|
||||||
| {
|
|
||||||
_id: string;
|
|
||||||
}
|
|
||||||
| boolean;
|
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
pinnedAt?: string | Date;
|
pinnedAt?: string | Date;
|
||||||
pinnedBy?: {
|
pinnedBy?: {
|
||||||
|
@ -120,11 +116,6 @@ export interface IMessage extends IMessageFromServer {
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
status?: number;
|
status?: number;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
starred?:
|
|
||||||
| {
|
|
||||||
_id: string;
|
|
||||||
}
|
|
||||||
| boolean;
|
|
||||||
editedBy?: IEditedBy;
|
editedBy?: IEditedBy;
|
||||||
reactions?: IReaction[];
|
reactions?: IReaction[];
|
||||||
role?: string;
|
role?: string;
|
||||||
|
@ -144,6 +135,8 @@ export interface IMessage extends IMessageFromServer {
|
||||||
tshow?: boolean;
|
tshow?: boolean;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
subscription?: { id: string };
|
subscription?: { id: string };
|
||||||
|
user?: string;
|
||||||
|
editedAt?: string | Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TMessageModel = IMessage & Model;
|
export type TMessageModel = IMessage & Model;
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
import rocketchat from '../lib/rocketchat';
|
|
||||||
|
|
||||||
export type TRocketChat = typeof rocketchat;
|
|
||||||
|
|
||||||
export interface IRocketChat extends TRocketChat {
|
|
||||||
closeListener: any;
|
|
||||||
usersListener: any;
|
|
||||||
notifyAllListener: any;
|
|
||||||
rolesListener: any;
|
|
||||||
notifyLoggedListener: any;
|
|
||||||
activeUsers: any;
|
|
||||||
_setUserTimer: any;
|
|
||||||
connectedListener: any;
|
|
||||||
connectingListener: any;
|
|
||||||
connectTimeout: any;
|
|
||||||
sdk: any;
|
|
||||||
activeUsersSubTimeout: any;
|
|
||||||
roomsSub: any;
|
|
||||||
}
|
|
|
@ -59,6 +59,20 @@ export interface IRoom {
|
||||||
waitingResponse?: boolean;
|
waitingResponse?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IRoomSettings {
|
||||||
|
roomName?: string;
|
||||||
|
roomAvatar?: string;
|
||||||
|
roomDescription?: string;
|
||||||
|
roomTopic?: string;
|
||||||
|
roomAnnouncement?: string;
|
||||||
|
roomType?: SubscriptionType;
|
||||||
|
readOnly?: boolean;
|
||||||
|
reactWhenReadOnly?: boolean;
|
||||||
|
systemMessages?: string[];
|
||||||
|
joinCode?: string;
|
||||||
|
encrypted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export enum OmnichannelSourceType {
|
export enum OmnichannelSourceType {
|
||||||
WIDGET = 'widget',
|
WIDGET = 'widget',
|
||||||
EMAIL = 'email',
|
EMAIL = 'email',
|
||||||
|
|
|
@ -22,7 +22,6 @@ export * from './IUser';
|
||||||
export * from './IServer';
|
export * from './IServer';
|
||||||
export * from './ILoggedUser';
|
export * from './ILoggedUser';
|
||||||
export * from './IServerHistory';
|
export * from './IServerHistory';
|
||||||
export * from './IRocketChat';
|
|
||||||
export * from './ICertificate';
|
export * from './ICertificate';
|
||||||
export * from './IUrl';
|
export * from './IUrl';
|
||||||
export * from './ICredentials';
|
export * from './ICredentials';
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useAppSelector';
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { TypedUseSelectorHook, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { IApplicationState } from '../../definitions';
|
||||||
|
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<IApplicationState> = useSelector;
|
|
@ -6,20 +6,24 @@ import { store as reduxStore } from '../store/auxStore';
|
||||||
import { setActiveUsers } from '../../actions/activeUsers';
|
import { setActiveUsers } from '../../actions/activeUsers';
|
||||||
import { setUser } from '../../actions/login';
|
import { setUser } from '../../actions/login';
|
||||||
import database from '../database';
|
import database from '../database';
|
||||||
import { IRocketChat, IUser } from '../../definitions';
|
import { IUser } from '../../definitions';
|
||||||
import sdk from '../services/sdk';
|
import sdk from '../services/sdk';
|
||||||
import { compareServerVersion } from './helpers/compareServerVersion';
|
import { compareServerVersion } from './helpers/compareServerVersion';
|
||||||
|
|
||||||
export function subscribeUsersPresence(this: IRocketChat) {
|
export const _activeUsersSubTimeout: { activeUsersSubTimeout: boolean | ReturnType<typeof setTimeout> } = {
|
||||||
|
activeUsersSubTimeout: false
|
||||||
|
};
|
||||||
|
|
||||||
|
export function subscribeUsersPresence() {
|
||||||
const serverVersion = reduxStore.getState().server.version as string;
|
const serverVersion = reduxStore.getState().server.version as string;
|
||||||
|
|
||||||
// if server is lower than 1.1.0
|
// if server is lower than 1.1.0
|
||||||
if (compareServerVersion(serverVersion, 'lowerThan', '1.1.0')) {
|
if (compareServerVersion(serverVersion, 'lowerThan', '1.1.0')) {
|
||||||
if (this.activeUsersSubTimeout) {
|
if (_activeUsersSubTimeout.activeUsersSubTimeout) {
|
||||||
clearTimeout(this.activeUsersSubTimeout);
|
clearTimeout(_activeUsersSubTimeout.activeUsersSubTimeout as number);
|
||||||
this.activeUsersSubTimeout = false;
|
_activeUsersSubTimeout.activeUsersSubTimeout = false;
|
||||||
}
|
}
|
||||||
this.activeUsersSubTimeout = setTimeout(() => {
|
_activeUsersSubTimeout.activeUsersSubTimeout = setTimeout(() => {
|
||||||
sdk.subscribe('activeUsers');
|
sdk.subscribe('activeUsers');
|
||||||
}, 5000);
|
}, 5000);
|
||||||
} else if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
} else if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { LISTENER } from '../../../containers/Toast';
|
||||||
|
import EventEmitter from '../../../utils/events';
|
||||||
|
|
||||||
|
export const showToast = (message: string): void => EventEmitter.emit(LISTENER, { message });
|
|
@ -34,3 +34,4 @@ export * from './userPreferences';
|
||||||
export * from './userPreferencesMethods';
|
export * from './userPreferencesMethods';
|
||||||
export * from './crashReport';
|
export * from './crashReport';
|
||||||
export * from './parseSettings';
|
export * from './parseSettings';
|
||||||
|
export * from './subscribeRooms';
|
||||||
|
|
|
@ -8,11 +8,13 @@ import { BASIC_AUTH_KEY } from '../../utils/fetch';
|
||||||
import database, { getDatabase } from '../database';
|
import database, { getDatabase } from '../database';
|
||||||
import { isSsl } from '../../utils/url';
|
import { isSsl } from '../../utils/url';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { ICertificate, IRocketChat } from '../../definitions';
|
import { ICertificate } from '../../definitions';
|
||||||
import sdk from '../services/sdk';
|
import sdk from '../services/sdk';
|
||||||
import { CURRENT_SERVER, E2E_PRIVATE_KEY, E2E_PUBLIC_KEY, E2E_RANDOM_PASSWORD_KEY, TOKEN_KEY } from '../constants';
|
import { CURRENT_SERVER, E2E_PRIVATE_KEY, E2E_PUBLIC_KEY, E2E_RANDOM_PASSWORD_KEY, TOKEN_KEY } from '../constants';
|
||||||
import UserPreferences from './userPreferences';
|
import UserPreferences from './userPreferences';
|
||||||
import { Services } from '../services';
|
import { Services } from '../services';
|
||||||
|
import { roomsSubscription } from './subscriptions/rooms';
|
||||||
|
import { _activeUsersSubTimeout } from '.';
|
||||||
|
|
||||||
function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) {
|
function removeServerKeys({ server, userId }: { server: string; userId?: string | null }) {
|
||||||
UserPreferences.removeItem(`${TOKEN_KEY}-${server}`);
|
UserPreferences.removeItem(`${TOKEN_KEY}-${server}`);
|
||||||
|
@ -98,15 +100,14 @@ export async function removeServer({ server }: { server: string }): Promise<void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logout(this: IRocketChat, { server }: { server: string }): Promise<void> {
|
export async function logout({ server }: { server: string }): Promise<void> {
|
||||||
if (this.roomsSub) {
|
if (roomsSubscription?.stop) {
|
||||||
this.roomsSub.stop();
|
roomsSubscription.stop();
|
||||||
this.roomsSub = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.activeUsersSubTimeout) {
|
if (_activeUsersSubTimeout.activeUsersSubTimeout) {
|
||||||
clearTimeout(this.activeUsersSubTimeout);
|
clearTimeout(_activeUsersSubTimeout.activeUsersSubTimeout as number);
|
||||||
this.activeUsersSubTimeout = false;
|
_activeUsersSubTimeout.activeUsersSubTimeout = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,16 +2,35 @@ import { InteractionManager } from 'react-native';
|
||||||
|
|
||||||
import { setActiveUsers } from '../../actions/activeUsers';
|
import { setActiveUsers } from '../../actions/activeUsers';
|
||||||
import { setUser } from '../../actions/login';
|
import { setUser } from '../../actions/login';
|
||||||
|
import { IUser } from '../../definitions';
|
||||||
import { store as reduxStore } from '../store/auxStore';
|
import { store as reduxStore } from '../store/auxStore';
|
||||||
import { compareServerVersion } from './helpers/compareServerVersion';
|
import { compareServerVersion } from './helpers/compareServerVersion';
|
||||||
|
|
||||||
// TODO
|
export interface IActiveUsers {
|
||||||
export function _setUser(this: any, ddpMessage: { fields: any; id: any; cleared: any }) {
|
[key: string]: { status: string; statusText?: string } | string | boolean;
|
||||||
this.activeUsers = this.activeUsers || {};
|
msg: string;
|
||||||
|
collection: string;
|
||||||
|
id: string;
|
||||||
|
cleared: boolean;
|
||||||
|
fields: {
|
||||||
|
emails: {
|
||||||
|
address: string;
|
||||||
|
verified: boolean;
|
||||||
|
}[];
|
||||||
|
username: string;
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const _activeUsers = { activeUsers: {} as IActiveUsers };
|
||||||
|
export const _setUserTimer: { setUserTimer: null | ReturnType<typeof setTimeout> } = { setUserTimer: null };
|
||||||
|
|
||||||
|
export function _setUser(ddpMessage: IActiveUsers): void {
|
||||||
|
_activeUsers.activeUsers = _activeUsers.activeUsers || {};
|
||||||
const { user } = reduxStore.getState().login;
|
const { user } = reduxStore.getState().login;
|
||||||
|
|
||||||
if (ddpMessage.fields && user && user.id === ddpMessage.id) {
|
if (ddpMessage.fields && user && user.id === ddpMessage.id) {
|
||||||
reduxStore.dispatch(setUser(ddpMessage.fields));
|
reduxStore.dispatch(setUser(ddpMessage.fields as Partial<IUser>));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ddpMessage.cleared && user && user.id === ddpMessage.id) {
|
if (ddpMessage.cleared && user && user.id === ddpMessage.id) {
|
||||||
|
@ -20,21 +39,22 @@ export function _setUser(this: any, ddpMessage: { fields: any; id: any; cleared:
|
||||||
|
|
||||||
const serverVersion = reduxStore.getState().server.version;
|
const serverVersion = reduxStore.getState().server.version;
|
||||||
if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) {
|
||||||
if (!this._setUserTimer) {
|
if (!_setUserTimer.setUserTimer) {
|
||||||
this._setUserTimer = setTimeout(() => {
|
_setUserTimer.setUserTimer = setTimeout(() => {
|
||||||
const activeUsersBatch = this.activeUsers;
|
const activeUsersBatch = _activeUsers.activeUsers;
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
// @ts-ignore
|
||||||
reduxStore.dispatch(setActiveUsers(activeUsersBatch));
|
reduxStore.dispatch(setActiveUsers(activeUsersBatch));
|
||||||
});
|
});
|
||||||
this._setUserTimer = null;
|
_setUserTimer.setUserTimer = null;
|
||||||
return (this.activeUsers = {});
|
_activeUsers.activeUsers = {} as IActiveUsers;
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ddpMessage.fields) {
|
if (!ddpMessage.fields) {
|
||||||
this.activeUsers[ddpMessage.id] = { status: 'offline' };
|
_activeUsers.activeUsers[ddpMessage.id] = { status: 'offline' };
|
||||||
} else if (ddpMessage.fields.status) {
|
} else if (ddpMessage.fields.status) {
|
||||||
this.activeUsers[ddpMessage.id] = { status: ddpMessage.fields.status };
|
_activeUsers.activeUsers[ddpMessage.id] = { status: ddpMessage.fields.status };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import subscribeRoomsTmp from './subscriptions/rooms';
|
import subscribeRoomsTmp, { roomsSubscription } from './subscriptions/rooms';
|
||||||
|
|
||||||
// TODO: remove this
|
export async function subscribeRooms(): Promise<void> {
|
||||||
export async function subscribeRooms(this: any) {
|
if (!roomsSubscription?.stop) {
|
||||||
if (!this.roomsSub) {
|
|
||||||
try {
|
try {
|
||||||
// TODO: We need to change this naming. Maybe move this logic to the SDK?
|
await subscribeRoomsTmp();
|
||||||
this.roomsSub = await subscribeRoomsTmp.call(this);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this
|
export function unsubscribeRooms(): void {
|
||||||
export function unsubscribeRooms(this: any) {
|
if (roomsSubscription?.stop) {
|
||||||
if (this.roomsSub) {
|
roomsSubscription.stop();
|
||||||
this.roomsSub.stop();
|
|
||||||
this.roomsSub = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ let queue: { [key: string]: ISubscription | IRoom } = {};
|
||||||
let subTimer: number | null | false = null;
|
let subTimer: number | null | false = null;
|
||||||
const WINDOW_TIME = 500;
|
const WINDOW_TIME = 500;
|
||||||
|
|
||||||
|
export let roomsSubscription: { stop: () => void } | null = null;
|
||||||
|
|
||||||
const createOrUpdateSubscription = async (subscription: ISubscription, room: IServerRoom | IRoom) => {
|
const createOrUpdateSubscription = async (subscription: ISubscription, room: IServerRoom | IRoom) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
@ -404,6 +406,7 @@ export default function subscribeRooms() {
|
||||||
clearTimeout(subTimer);
|
clearTimeout(subTimer);
|
||||||
subTimer = false;
|
subTimer = false;
|
||||||
}
|
}
|
||||||
|
roomsSubscription = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
streamListener = sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
|
streamListener = sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
|
||||||
|
@ -412,10 +415,8 @@ export default function subscribeRooms() {
|
||||||
// set the server that started this task
|
// set the server that started this task
|
||||||
subServer = sdk.current.client.host;
|
subServer = sdk.current.client.host;
|
||||||
sdk.current.subscribeNotifyUser().catch((e: unknown) => console.log(e));
|
sdk.current.subscribeNotifyUser().catch((e: unknown) => console.log(e));
|
||||||
|
roomsSubscription = { stop: () => stop() };
|
||||||
return {
|
return null;
|
||||||
stop: () => stop()
|
|
||||||
};
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { IApplicationState, TServerModel } from '../../definitions';
|
import { IApplicationState, TServerModel } from '../../definitions';
|
||||||
import database from '../database';
|
import database from '../database';
|
||||||
|
import { useAppSelector } from '../hooks';
|
||||||
|
|
||||||
export default function useServer() {
|
export default function useServer() {
|
||||||
const [server, setServer] = useState<TServerModel | null>(null);
|
const [server, setServer] = useState<TServerModel | null>(null);
|
||||||
const shareServer = useSelector((state: IApplicationState) => state.share.server.server);
|
const shareServer = useAppSelector((state: IApplicationState) => state.share.server.server);
|
||||||
const appServer = useSelector((state: IApplicationState) => state.server.server);
|
const appServer = useAppSelector((state: IApplicationState) => state.server.server);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function init() {
|
async function init() {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import EJSON from 'ejson';
|
||||||
import { store } from '../store/auxStore';
|
import { store } from '../store/auxStore';
|
||||||
import { deepLinkingOpen } from '../../actions/deepLinking';
|
import { deepLinkingOpen } from '../../actions/deepLinking';
|
||||||
import { isFDroidBuild } from '../constants';
|
import { isFDroidBuild } from '../constants';
|
||||||
import PushNotification from './push';
|
import { deviceToken, pushNotificationConfigure, setNotificationsBadgeCount } from './push';
|
||||||
import { INotification, SubscriptionType } from '../../definitions';
|
import { INotification, SubscriptionType } from '../../definitions';
|
||||||
|
|
||||||
interface IEjson {
|
interface IEjson {
|
||||||
|
@ -47,11 +47,11 @@ export const onNotification = (push: INotification): void => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDeviceToken = (): string => PushNotification.getDeviceToken();
|
export const getDeviceToken = (): string => deviceToken;
|
||||||
export const setBadgeCount = (count?: number): void => PushNotification.setBadgeCount(count);
|
export const setBadgeCount = (count?: number): void => setNotificationsBadgeCount(count);
|
||||||
export const initializePushNotifications = (): Promise<INotification> | undefined => {
|
export const initializePushNotifications = (): Promise<INotification> | undefined => {
|
||||||
if (!isFDroidBuild) {
|
if (!isFDroidBuild) {
|
||||||
setBadgeCount();
|
setBadgeCount();
|
||||||
return PushNotification.configure(onNotification);
|
return pushNotificationConfigure(onNotification);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,16 +13,18 @@ import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { store as reduxStore } from '../store/auxStore';
|
import { store as reduxStore } from '../store/auxStore';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
class PushNotification {
|
export let deviceToken = '';
|
||||||
onNotification: (notification: any) => void;
|
|
||||||
deviceToken: string;
|
export const setNotificationsBadgeCount = (count = 0): void => {
|
||||||
constructor() {
|
if (isIOS) {
|
||||||
this.onNotification = () => {};
|
Notifications.ios.setBadgeCount(count);
|
||||||
this.deviceToken = '';
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pushNotificationConfigure = (onNotification: (notification: INotification) => void): Promise<any> => {
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
// init
|
// init
|
||||||
Notifications.ios.registerRemoteNotifications();
|
Notifications.ios.registerRemoteNotifications();
|
||||||
|
|
||||||
// setCategories
|
// setCategories
|
||||||
const notificationAction = new NotificationAction('REPLY_ACTION', 'background', I18n.t('Reply'), true, {
|
const notificationAction = new NotificationAction('REPLY_ACTION', 'background', I18n.t('Reply'), true, {
|
||||||
buttonTitle: I18n.t('Reply'),
|
buttonTitle: I18n.t('Reply'),
|
||||||
|
@ -36,7 +38,7 @@ class PushNotification {
|
||||||
}
|
}
|
||||||
|
|
||||||
Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
|
Notifications.events().registerRemoteNotificationsRegistered((event: Registered) => {
|
||||||
this.deviceToken = event.deviceToken;
|
deviceToken = event.deviceToken;
|
||||||
});
|
});
|
||||||
|
|
||||||
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
|
Notifications.events().registerRemoteNotificationsRegistrationFailed((event: RegistrationError) => {
|
||||||
|
@ -54,10 +56,10 @@ class PushNotification {
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
const { background } = reduxStore.getState().app;
|
const { background } = reduxStore.getState().app;
|
||||||
if (background) {
|
if (background) {
|
||||||
this.onNotification(notification);
|
onNotification(notification);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.onNotification(notification);
|
onNotification(notification);
|
||||||
}
|
}
|
||||||
completion();
|
completion();
|
||||||
});
|
});
|
||||||
|
@ -67,22 +69,6 @@ class PushNotification {
|
||||||
completion({ alert: true, sound: true, badge: false });
|
completion({ alert: true, sound: true, badge: false });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
getDeviceToken() {
|
|
||||||
return this.deviceToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBadgeCount = (count = 0) => {
|
|
||||||
if (isIOS) {
|
|
||||||
Notifications.ios.setBadgeCount(count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
configure(onNotification: (notification: INotification) => void): Promise<any> {
|
|
||||||
this.onNotification = onNotification;
|
|
||||||
return Notifications.getInitialNotification();
|
return Notifications.getInitialNotification();
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default new PushNotification();
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { _setUser } from './methods/setUser';
|
|
||||||
import { logout } from './methods/logout';
|
|
||||||
import { subscribeRooms, unsubscribeRooms } from './methods/subscribeRooms';
|
|
||||||
import { subscribeUsersPresence } from './methods/getUsersPresence';
|
|
||||||
import { connect } from './services/connect';
|
|
||||||
|
|
||||||
const RocketChat = {
|
|
||||||
logout,
|
|
||||||
subscribeRooms,
|
|
||||||
unsubscribeRooms,
|
|
||||||
_setUser,
|
|
||||||
subscribeUsersPresence,
|
|
||||||
connect
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RocketChat;
|
|
|
@ -1,5 +1,5 @@
|
||||||
import RNFetchBlob from 'rn-fetch-blob';
|
import RNFetchBlob from 'rn-fetch-blob';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings, Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
|
||||||
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
|
||||||
import { InteractionManager } from 'react-native';
|
import { InteractionManager } from 'react-native';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
@ -14,8 +14,7 @@ import { store } from '../store/auxStore';
|
||||||
import { loginRequest, setLoginServices, setUser } from '../../actions/login';
|
import { loginRequest, setLoginServices, setUser } from '../../actions/login';
|
||||||
import sdk from './sdk';
|
import sdk from './sdk';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import RocketChat from '../rocketchat';
|
import { ICredentials, ILoggedUser, STATUSES } from '../../definitions';
|
||||||
import { ICredentials, ILoggedUser, IRocketChat, STATUSES } from '../../definitions';
|
|
||||||
import { isIOS } from '../../utils/deviceInfo';
|
import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { connectRequest, connectSuccess, disconnect as disconnectAction } from '../../actions/connect';
|
import { connectRequest, connectSuccess, disconnect as disconnectAction } from '../../actions/connect';
|
||||||
import { updatePermission } from '../../actions/permissions';
|
import { updatePermission } from '../../actions/permissions';
|
||||||
|
@ -24,7 +23,8 @@ import { updateSettings } from '../../actions/settings';
|
||||||
import { defaultSettings, MIN_ROCKETCHAT_VERSION } from '../constants';
|
import { defaultSettings, MIN_ROCKETCHAT_VERSION } from '../constants';
|
||||||
import { compareServerVersion } from '../methods/helpers/compareServerVersion';
|
import { compareServerVersion } from '../methods/helpers/compareServerVersion';
|
||||||
import { onRolesChanged } from '../methods/getRoles';
|
import { onRolesChanged } from '../methods/getRoles';
|
||||||
import { getSettings } from '../methods';
|
import { isSsl } from '../../utils/url';
|
||||||
|
import { getSettings, IActiveUsers, unsubscribeRooms, _activeUsers, _setUser, _setUserTimer } from '../methods';
|
||||||
|
|
||||||
interface IServices {
|
interface IServices {
|
||||||
[index: string]: string | boolean;
|
[index: string]: string | boolean;
|
||||||
|
@ -35,11 +35,15 @@ interface IServices {
|
||||||
service: string;
|
service: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Remove `this` context
|
let connectingListener: any;
|
||||||
function connect(
|
let connectedListener: any;
|
||||||
this: IRocketChat,
|
let closeListener: any;
|
||||||
{ server, logoutOnError = false }: { server: string; logoutOnError: boolean }
|
let usersListener: any;
|
||||||
): Promise<void> {
|
let notifyAllListener: any;
|
||||||
|
let rolesListener: any;
|
||||||
|
let notifyLoggedListener: any;
|
||||||
|
|
||||||
|
function connect({ server, logoutOnError = false }: { server: string; logoutOnError: boolean }): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
if (sdk.current?.client?.host === server) {
|
if (sdk.current?.client?.host === server) {
|
||||||
return resolve();
|
return resolve();
|
||||||
|
@ -49,39 +53,40 @@ function connect(
|
||||||
|
|
||||||
store.dispatch(connectRequest());
|
store.dispatch(connectRequest());
|
||||||
|
|
||||||
if (this.connectTimeout) {
|
// It's not called anywhere else
|
||||||
clearTimeout(this.connectTimeout);
|
// if (this.connectTimeout) {
|
||||||
|
// clearTimeout(this.connectTimeout);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (connectingListener) {
|
||||||
|
connectingListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.connectingListener) {
|
if (connectedListener) {
|
||||||
this.connectingListener.then(stopListener);
|
connectedListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.connectedListener) {
|
if (closeListener) {
|
||||||
this.connectedListener.then(stopListener);
|
closeListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.closeListener) {
|
if (usersListener) {
|
||||||
this.closeListener.then(stopListener);
|
usersListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.usersListener) {
|
if (notifyAllListener) {
|
||||||
this.usersListener.then(stopListener);
|
notifyAllListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.notifyAllListener) {
|
if (rolesListener) {
|
||||||
this.notifyAllListener.then(stopListener);
|
rolesListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rolesListener) {
|
if (notifyLoggedListener) {
|
||||||
this.rolesListener.then(stopListener);
|
notifyLoggedListener.then(stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.notifyLoggedListener) {
|
unsubscribeRooms();
|
||||||
this.notifyLoggedListener.then(stopListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.unsubscribeRooms();
|
|
||||||
|
|
||||||
EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
|
EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
|
||||||
|
|
||||||
|
@ -97,11 +102,11 @@ function connect(
|
||||||
console.log('connect error', err);
|
console.log('connect error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connectingListener = sdk.current.onStreamData('connecting', () => {
|
connectingListener = sdk.current.onStreamData('connecting', () => {
|
||||||
store.dispatch(connectRequest());
|
store.dispatch(connectRequest());
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connectedListener = sdk.current.onStreamData('connected', () => {
|
connectedListener = sdk.current.onStreamData('connected', () => {
|
||||||
const { connected } = store.getState().meteor;
|
const { connected } = store.getState().meteor;
|
||||||
if (connected) {
|
if (connected) {
|
||||||
return;
|
return;
|
||||||
|
@ -113,16 +118,16 @@ function connect(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.closeListener = sdk.current.onStreamData('close', () => {
|
closeListener = sdk.current.onStreamData('close', () => {
|
||||||
store.dispatch(disconnectAction());
|
store.dispatch(disconnectAction());
|
||||||
});
|
});
|
||||||
|
|
||||||
this.usersListener = sdk.current.onStreamData(
|
usersListener = sdk.current.onStreamData(
|
||||||
'users',
|
'users',
|
||||||
protectedFunction((ddpMessage: any) => RocketChat._setUser(ddpMessage))
|
protectedFunction((ddpMessage: any) => _setUser(ddpMessage))
|
||||||
);
|
);
|
||||||
|
|
||||||
this.notifyAllListener = sdk.current.onStreamData(
|
notifyAllListener = sdk.current.onStreamData(
|
||||||
'stream-notify-all',
|
'stream-notify-all',
|
||||||
protectedFunction(async (ddpMessage: { fields: { args?: any; eventName: string } }) => {
|
protectedFunction(async (ddpMessage: { fields: { args?: any; eventName: string } }) => {
|
||||||
const { eventName } = ddpMessage.fields;
|
const { eventName } = ddpMessage.fields;
|
||||||
|
@ -150,7 +155,7 @@ function connect(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.rolesListener = sdk.current.onStreamData(
|
rolesListener = sdk.current.onStreamData(
|
||||||
'stream-roles',
|
'stream-roles',
|
||||||
protectedFunction((ddpMessage: any) => onRolesChanged(ddpMessage))
|
protectedFunction((ddpMessage: any) => onRolesChanged(ddpMessage))
|
||||||
);
|
);
|
||||||
|
@ -171,27 +176,29 @@ function connect(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.notifyLoggedListener = sdk.current.onStreamData(
|
notifyLoggedListener = sdk.current.onStreamData(
|
||||||
'stream-notify-logged',
|
'stream-notify-logged',
|
||||||
protectedFunction(async (ddpMessage: { fields: { args?: any; eventName?: any } }) => {
|
protectedFunction(async (ddpMessage: { fields: { args?: any; eventName?: any } }) => {
|
||||||
const { eventName } = ddpMessage.fields;
|
const { eventName } = ddpMessage.fields;
|
||||||
|
|
||||||
// `user-status` event is deprecated after RC 4.1 in favor of `stream-user-presence/${uid}`
|
// `user-status` event is deprecated after RC 4.1 in favor of `stream-user-presence/${uid}`
|
||||||
if (/user-status/.test(eventName)) {
|
if (/user-status/.test(eventName)) {
|
||||||
this.activeUsers = this.activeUsers || {};
|
_activeUsers.activeUsers = _activeUsers.activeUsers || {};
|
||||||
if (!this._setUserTimer) {
|
if (!_setUserTimer.setUserTimer) {
|
||||||
this._setUserTimer = setTimeout(() => {
|
_setUserTimer.setUserTimer = setTimeout(() => {
|
||||||
const activeUsersBatch = this.activeUsers;
|
const activeUsersBatch = _activeUsers.activeUsers;
|
||||||
InteractionManager.runAfterInteractions(() => {
|
InteractionManager.runAfterInteractions(() => {
|
||||||
|
// @ts-ignore
|
||||||
store.dispatch(setActiveUsers(activeUsersBatch));
|
store.dispatch(setActiveUsers(activeUsersBatch));
|
||||||
});
|
});
|
||||||
this._setUserTimer = null;
|
_setUserTimer.setUserTimer = null;
|
||||||
return (this.activeUsers = {});
|
_activeUsers.activeUsers = {} as IActiveUsers;
|
||||||
|
return null;
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
const userStatus = ddpMessage.fields.args[0];
|
const userStatus = ddpMessage.fields.args[0];
|
||||||
const [id, , status, statusText] = userStatus;
|
const [id, , status, statusText] = userStatus;
|
||||||
this.activeUsers[id] = { status: STATUSES[status], statusText };
|
_activeUsers.activeUsers[id] = { status: STATUSES[status], statusText };
|
||||||
|
|
||||||
const { user: loggedUser } = store.getState().login;
|
const { user: loggedUser } = store.getState().login;
|
||||||
if (loggedUser && loggedUser.id === id) {
|
if (loggedUser && loggedUser.id === id) {
|
||||||
|
@ -419,10 +426,10 @@ async function getServerInfo(server: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWebsocketInfo({ server }: { server: string }) {
|
async function getWebsocketInfo({ server }: { server: string }) {
|
||||||
sdk.initialize(server);
|
const websocketSdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl: isSsl(server) });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sdk.current.connect();
|
await websocketSdk.connect();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.message && err.message.includes('400')) {
|
if (err.message && err.message.includes('400')) {
|
||||||
return {
|
return {
|
||||||
|
@ -432,7 +439,7 @@ async function getWebsocketInfo({ server }: { server: string }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sdk.disconnect();
|
websocketSdk.disconnect();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true
|
success: true
|
||||||
|
|
|
@ -16,10 +16,9 @@ import { TParams } from '../../definitions/ILivechatEditView';
|
||||||
import { store as reduxStore } from '../store/auxStore';
|
import { store as reduxStore } from '../store/auxStore';
|
||||||
import { getDeviceToken } from '../notifications';
|
import { getDeviceToken } from '../notifications';
|
||||||
import { getBundleId, isIOS } from '../../utils/deviceInfo';
|
import { getBundleId, isIOS } from '../../utils/deviceInfo';
|
||||||
import { RoomTypes, roomTypeToApiType } from '../methods';
|
import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods';
|
||||||
import sdk from './sdk';
|
import sdk from './sdk';
|
||||||
import { compareServerVersion } from '../methods/helpers/compareServerVersion';
|
import { compareServerVersion } from '../methods/helpers/compareServerVersion';
|
||||||
import RocketChat from '../rocketchat';
|
|
||||||
|
|
||||||
export const createChannel = ({
|
export const createChannel = ({
|
||||||
name,
|
name,
|
||||||
|
@ -254,7 +253,7 @@ export const markAsUnread = ({ messageId }: { messageId: string }) =>
|
||||||
// RC 0.65.0
|
// RC 0.65.0
|
||||||
sdk.post('subscriptions.unread', { firstUnreadMessage: { _id: messageId } });
|
sdk.post('subscriptions.unread', { firstUnreadMessage: { _id: messageId } });
|
||||||
|
|
||||||
export const toggleStarMessage = (messageId: string, starred: boolean) => {
|
export const toggleStarMessage = (messageId: string, starred?: boolean) => {
|
||||||
if (starred) {
|
if (starred) {
|
||||||
// RC 0.59.0
|
// RC 0.59.0
|
||||||
return sdk.post('chat.unStarMessage', { messageId });
|
return sdk.post('chat.unStarMessage', { messageId });
|
||||||
|
@ -263,7 +262,7 @@ export const toggleStarMessage = (messageId: string, starred: boolean) => {
|
||||||
return sdk.post('chat.starMessage', { messageId });
|
return sdk.post('chat.starMessage', { messageId });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const togglePinMessage = (messageId: string, pinned: boolean) => {
|
export const togglePinMessage = (messageId: string, pinned?: boolean) => {
|
||||||
if (pinned) {
|
if (pinned) {
|
||||||
// RC 0.59.0
|
// RC 0.59.0
|
||||||
return sdk.post('chat.unPinMessage', { messageId });
|
return sdk.post('chat.unPinMessage', { messageId });
|
||||||
|
@ -807,7 +806,7 @@ export const emitTyping = (room: IRoom, typing = true) => {
|
||||||
|
|
||||||
export function e2eResetOwnKey(): Promise<boolean | {}> {
|
export function e2eResetOwnKey(): Promise<boolean | {}> {
|
||||||
// {} when TOTP is enabled
|
// {} when TOTP is enabled
|
||||||
RocketChat.unsubscribeRooms();
|
unsubscribeRooms();
|
||||||
|
|
||||||
// RC 0.72.0
|
// RC 0.72.0
|
||||||
return sdk.methodCallWrapper('e2e.resetOwnE2EKey');
|
return sdk.methodCallWrapper('e2e.resetOwnE2EKey');
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { appStart } from '../actions/app';
|
||||||
import { selectServerRequest, serverFinishAdd } from '../actions/server';
|
import { selectServerRequest, serverFinishAdd } from '../actions/server';
|
||||||
import { loginFailure, loginSuccess, logout as logoutAction, setUser } from '../actions/login';
|
import { loginFailure, loginSuccess, logout as logoutAction, setUser } from '../actions/login';
|
||||||
import { roomsRequest } from '../actions/rooms';
|
import { roomsRequest } from '../actions/rooms';
|
||||||
import RocketChat from '../lib/rocketchat';
|
|
||||||
import log, { events, logEvent } from '../utils/log';
|
import log, { events, logEvent } from '../utils/log';
|
||||||
import I18n, { setLanguage } from '../i18n';
|
import I18n, { setLanguage } from '../i18n';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
|
@ -30,14 +29,16 @@ import {
|
||||||
getSlashCommands,
|
getSlashCommands,
|
||||||
getUserPresence,
|
getUserPresence,
|
||||||
isOmnichannelModuleAvailable,
|
isOmnichannelModuleAvailable,
|
||||||
subscribeSettings
|
logout,
|
||||||
|
subscribeSettings,
|
||||||
|
subscribeUsersPresence
|
||||||
} from '../lib/methods';
|
} from '../lib/methods';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
|
||||||
const getServer = state => state.server.server;
|
const getServer = state => state.server.server;
|
||||||
const loginWithPasswordCall = args => Services.loginWithPassword(args);
|
const loginWithPasswordCall = args => Services.loginWithPassword(args);
|
||||||
const loginCall = (credentials, isFromWebView) => Services.login(credentials, isFromWebView);
|
const loginCall = (credentials, isFromWebView) => Services.login(credentials, isFromWebView);
|
||||||
const logoutCall = args => RocketChat.logout(args);
|
const logoutCall = args => logout(args);
|
||||||
|
|
||||||
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false, isFromWebView = false }) {
|
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false, isFromWebView = false }) {
|
||||||
logEvent(events.LOGIN_DEFAULT_LOGIN);
|
logEvent(events.LOGIN_DEFAULT_LOGIN);
|
||||||
|
@ -114,7 +115,7 @@ const registerPushTokenFork = function* registerPushTokenFork() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchUsersPresenceFork = function* fetchUsersPresenceFork() {
|
const fetchUsersPresenceFork = function* fetchUsersPresenceFork() {
|
||||||
RocketChat.subscribeUsersPresence();
|
subscribeUsersPresence();
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchEnterpriseModulesFork = function* fetchEnterpriseModulesFork({ user }) {
|
const fetchEnterpriseModulesFork = function* fetchEnterpriseModulesFork({ user }) {
|
||||||
|
|
|
@ -7,9 +7,8 @@ import { roomsFailure, roomsRefresh, roomsSuccess } from '../actions/rooms';
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import mergeSubscriptionsRooms from '../lib/methods/helpers/mergeSubscriptionsRooms';
|
import mergeSubscriptionsRooms from '../lib/methods/helpers/mergeSubscriptionsRooms';
|
||||||
import RocketChat from '../lib/rocketchat';
|
|
||||||
import buildMessage from '../lib/methods/helpers/buildMessage';
|
import buildMessage from '../lib/methods/helpers/buildMessage';
|
||||||
import { getRooms } from '../lib/methods';
|
import { getRooms, subscribeRooms } from '../lib/methods';
|
||||||
|
|
||||||
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
|
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
|
@ -30,7 +29,7 @@ const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
|
||||||
const handleRoomsRequest = function* handleRoomsRequest({ params }) {
|
const handleRoomsRequest = function* handleRoomsRequest({ params }) {
|
||||||
try {
|
try {
|
||||||
const serversDB = database.servers;
|
const serversDB = database.servers;
|
||||||
RocketChat.subscribeRooms();
|
subscribeRooms();
|
||||||
const newRoomsUpdatedAt = new Date();
|
const newRoomsUpdatedAt = new Date();
|
||||||
let roomsUpdatedAt;
|
let roomsUpdatedAt;
|
||||||
const server = yield select(state => state.server.server);
|
const server = yield select(state => state.server.server);
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { selectServerFailure, selectServerRequest, selectServerSuccess, serverFa
|
||||||
import { clearSettings } from '../actions/settings';
|
import { clearSettings } from '../actions/settings';
|
||||||
import { clearUser, setUser } from '../actions/login';
|
import { clearUser, setUser } from '../actions/login';
|
||||||
import { clearActiveUsers } from '../actions/activeUsers';
|
import { clearActiveUsers } from '../actions/activeUsers';
|
||||||
import RocketChat from '../lib/rocketchat';
|
|
||||||
import database from '../lib/database';
|
import database from '../lib/database';
|
||||||
import log, { logServerVersion } from '../utils/log';
|
import log, { logServerVersion } from '../utils/log';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
@ -25,6 +24,7 @@ import { RootEnum } from '../definitions';
|
||||||
import { CERTIFICATE_KEY, CURRENT_SERVER, TOKEN_KEY } from '../lib/constants';
|
import { CERTIFICATE_KEY, CURRENT_SERVER, TOKEN_KEY } from '../lib/constants';
|
||||||
import { getLoginSettings, setCustomEmojis, setEnterpriseModules, setPermissions, setRoles, setSettings } from '../lib/methods';
|
import { getLoginSettings, setCustomEmojis, setEnterpriseModules, setPermissions, setRoles, setSettings } from '../lib/methods';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
import { connect } from '../lib/services/connect';
|
||||||
|
|
||||||
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
|
||||||
try {
|
try {
|
||||||
|
@ -115,11 +115,11 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
|
||||||
if (user) {
|
if (user) {
|
||||||
yield put(clearSettings());
|
yield put(clearSettings());
|
||||||
yield put(setUser(user));
|
yield put(setUser(user));
|
||||||
yield RocketChat.connect({ server, logoutOnError: true });
|
yield connect({ server, logoutOnError: true });
|
||||||
yield put(appStart({ root: RootEnum.ROOT_INSIDE }));
|
yield put(appStart({ root: RootEnum.ROOT_INSIDE }));
|
||||||
} else {
|
} else {
|
||||||
yield put(clearUser());
|
yield put(clearUser());
|
||||||
yield RocketChat.connect({ server });
|
yield connect({ server });
|
||||||
yield put(appStart({ root: RootEnum.ROOT_OUTSIDE }));
|
yield put(appStart({ root: RootEnum.ROOT_OUTSIDE }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { select, takeLatest } from 'redux-saga/effects';
|
import { select, takeLatest } from 'redux-saga/effects';
|
||||||
|
|
||||||
import Push from '../lib/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 { RootEnum } from '../definitions';
|
import { RootEnum } from '../definitions';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
import { setBadgeCount } from '../lib/notifications';
|
||||||
|
|
||||||
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
const appRoot = yield select(state => state.app.root);
|
const appRoot = yield select(state => state.app.root);
|
||||||
|
@ -20,7 +20,7 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
try {
|
try {
|
||||||
yield localAuthenticate(server.server);
|
yield localAuthenticate(server.server);
|
||||||
Services.checkAndReopen();
|
Services.checkAndReopen();
|
||||||
Push.setBadgeCount();
|
setBadgeCount();
|
||||||
return yield Services.setUserPresenceOnline();
|
return yield Services.setUserPresenceOnline();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
|
|
@ -101,7 +101,7 @@ const ChatsStackNavigator = () => {
|
||||||
options={SearchMessagesView.navigationOptions}
|
options={SearchMessagesView.navigationOptions}
|
||||||
/>
|
/>
|
||||||
<ChatsStack.Screen name='SelectedUsersView' component={SelectedUsersView} />
|
<ChatsStack.Screen name='SelectedUsersView' component={SelectedUsersView} />
|
||||||
<ChatsStack.Screen name='InviteUsersView' component={InviteUsersView} options={InviteUsersView.navigationOptions} />
|
<ChatsStack.Screen name='InviteUsersView' component={InviteUsersView} />
|
||||||
<ChatsStack.Screen
|
<ChatsStack.Screen
|
||||||
name='InviteUsersEditView'
|
name='InviteUsersEditView'
|
||||||
component={InviteUsersEditView}
|
component={InviteUsersEditView}
|
||||||
|
@ -131,7 +131,7 @@ const ChatsStackNavigator = () => {
|
||||||
component={AddExistingChannelView}
|
component={AddExistingChannelView}
|
||||||
options={AddExistingChannelView.navigationOptions}
|
options={AddExistingChannelView.navigationOptions}
|
||||||
/>
|
/>
|
||||||
<ChatsStack.Screen name='MarkdownTableView' component={MarkdownTableView} options={MarkdownTableView.navigationOptions} />
|
<ChatsStack.Screen name='MarkdownTableView' component={MarkdownTableView} />
|
||||||
<ChatsStack.Screen name='ReadReceiptsView' component={ReadReceiptsView} options={ReadReceiptsView.navigationOptions} />
|
<ChatsStack.Screen name='ReadReceiptsView' component={ReadReceiptsView} options={ReadReceiptsView.navigationOptions} />
|
||||||
<ChatsStack.Screen name='QueueListView' component={QueueListView} options={QueueListView.navigationOptions} />
|
<ChatsStack.Screen name='QueueListView' component={QueueListView} options={QueueListView.navigationOptions} />
|
||||||
<ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
|
<ChatsStack.Screen name='CannedResponsesListView' component={CannedResponsesListView} />
|
||||||
|
@ -198,7 +198,7 @@ const AdminPanelStackNavigator = () => {
|
||||||
return (
|
return (
|
||||||
<AdminPanelStack.Navigator
|
<AdminPanelStack.Navigator
|
||||||
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
|
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
|
||||||
<AdminPanelStack.Screen name='AdminPanelView' component={AdminPanelView} options={AdminPanelView.navigationOptions} />
|
<AdminPanelStack.Screen name='AdminPanelView' component={AdminPanelView} />
|
||||||
</AdminPanelStack.Navigator>
|
</AdminPanelStack.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -129,7 +129,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
||||||
options={SearchMessagesView.navigationOptions}
|
options={SearchMessagesView.navigationOptions}
|
||||||
/>
|
/>
|
||||||
<ModalStack.Screen name='SelectedUsersView' component={SelectedUsersView} />
|
<ModalStack.Screen name='SelectedUsersView' component={SelectedUsersView} />
|
||||||
<ModalStack.Screen name='InviteUsersView' component={InviteUsersView} options={InviteUsersView.navigationOptions} />
|
<ModalStack.Screen name='InviteUsersView' component={InviteUsersView} />
|
||||||
<ModalStack.Screen name='AddChannelTeamView' component={AddChannelTeamView} />
|
<ModalStack.Screen name='AddChannelTeamView' component={AddChannelTeamView} />
|
||||||
<ModalStack.Screen
|
<ModalStack.Screen
|
||||||
name='AddExistingChannelView'
|
name='AddExistingChannelView'
|
||||||
|
@ -166,7 +166,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
||||||
<ModalStack.Screen name='ThreadMessagesView' component={ThreadMessagesView} />
|
<ModalStack.Screen name='ThreadMessagesView' component={ThreadMessagesView} />
|
||||||
<ModalStack.Screen name='DiscussionsView' component={DiscussionsView} />
|
<ModalStack.Screen name='DiscussionsView' component={DiscussionsView} />
|
||||||
<ModalStack.Screen name='TeamChannelsView' component={TeamChannelsView} options={TeamChannelsView.navigationOptions} />
|
<ModalStack.Screen name='TeamChannelsView' component={TeamChannelsView} options={TeamChannelsView.navigationOptions} />
|
||||||
<ModalStack.Screen name='MarkdownTableView' component={MarkdownTableView} options={MarkdownTableView.navigationOptions} />
|
<ModalStack.Screen name='MarkdownTableView' component={MarkdownTableView} />
|
||||||
<ModalStack.Screen
|
<ModalStack.Screen
|
||||||
name='ReadReceiptsView'
|
name='ReadReceiptsView'
|
||||||
component={ReadReceiptsView}
|
component={ReadReceiptsView}
|
||||||
|
@ -196,11 +196,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
|
||||||
options={props => ProfileView.navigationOptions!({ ...props, isMasterDetail: true })}
|
options={props => ProfileView.navigationOptions!({ ...props, isMasterDetail: true })}
|
||||||
/>
|
/>
|
||||||
<ModalStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} />
|
<ModalStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} />
|
||||||
<ModalStack.Screen
|
<ModalStack.Screen name='AdminPanelView' component={AdminPanelView} />
|
||||||
name='AdminPanelView'
|
|
||||||
component={AdminPanelView}
|
|
||||||
options={props => AdminPanelView.navigationOptions!({ ...props, isMasterDetail: true })}
|
|
||||||
/>
|
|
||||||
<ModalStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} />
|
<ModalStack.Screen name='NewMessageView' component={NewMessageView} options={NewMessageView.navigationOptions} />
|
||||||
<ModalStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} />
|
<ModalStack.Screen name='SelectedUsersViewCreateChannel' component={SelectedUsersView} />
|
||||||
<ModalStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} />
|
<ModalStack.Screen name='CreateChannelView' component={CreateChannelView} options={CreateChannelView.navigationOptions} />
|
||||||
|
|
|
@ -28,7 +28,7 @@ const _OutsideStack = () => {
|
||||||
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
|
<Outside.Screen name='ForgotPasswordView' component={ForgotPasswordView} options={ForgotPasswordView.navigationOptions} />
|
||||||
<Outside.Screen name='SendEmailConfirmationView' component={SendEmailConfirmationView} />
|
<Outside.Screen name='SendEmailConfirmationView' component={SendEmailConfirmationView} />
|
||||||
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
|
<Outside.Screen name='RegisterView' component={RegisterView} options={RegisterView.navigationOptions} />
|
||||||
<Outside.Screen name='LegalView' component={LegalView} options={LegalView.navigationOptions} />
|
<Outside.Screen name='LegalView' component={LegalView} />
|
||||||
</Outside.Navigator>
|
</Outside.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ISubscription, SubscriptionType, TSubscriptionModel } from '../definiti
|
||||||
import { ICannedResponse } from '../definitions/ICannedResponse';
|
import { ICannedResponse } from '../definitions/ICannedResponse';
|
||||||
import { TDataSelect } from '../definitions/IDataSelect';
|
import { TDataSelect } from '../definitions/IDataSelect';
|
||||||
import { ModalStackParamList } from './MasterDetailStack/types';
|
import { ModalStackParamList } from './MasterDetailStack/types';
|
||||||
|
import { TThreadModel } from '../definitions';
|
||||||
|
|
||||||
export type ChatsStackParamList = {
|
export type ChatsStackParamList = {
|
||||||
ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>;
|
ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>;
|
||||||
|
@ -246,7 +247,7 @@ export type InsideStackParamList = {
|
||||||
serverInfo: IServer;
|
serverInfo: IServer;
|
||||||
text: string;
|
text: string;
|
||||||
room: TSubscriptionModel;
|
room: TSubscriptionModel;
|
||||||
thread: any; // TODO: Change
|
thread: TThreadModel;
|
||||||
};
|
};
|
||||||
ModalBlockView: {
|
ModalBlockView: {
|
||||||
data: any; // TODO: Change;
|
data: any; // TODO: Change;
|
||||||
|
|
|
@ -2,8 +2,6 @@ import { Platform } from 'react-native';
|
||||||
import DeviceInfo from 'react-native-device-info';
|
import DeviceInfo from 'react-native-device-info';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
|
|
||||||
import RocketChat from '../lib/rocketchat';
|
|
||||||
|
|
||||||
export type TMethods = 'POST' | 'GET' | 'DELETE' | 'PUT' | 'post' | 'get' | 'delete' | 'put';
|
export type TMethods = 'POST' | 'GET' | 'DELETE' | 'PUT' | 'post' | 'get' | 'delete' | 'put';
|
||||||
|
|
||||||
interface CustomHeaders {
|
interface CustomHeaders {
|
||||||
|
@ -46,13 +44,11 @@ export default (url: string, options: IOptions = {}): Promise<Response> => {
|
||||||
if (options && options.headers) {
|
if (options && options.headers) {
|
||||||
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };
|
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };
|
||||||
}
|
}
|
||||||
// TODO: Refactor when migrate rocketchat.js
|
// TODO: Check if this really works and if anyone else has complained about this problem.
|
||||||
// @ts-ignore
|
// if (RocketChat.controller) {
|
||||||
// WHAT?
|
// // @ts-ignore
|
||||||
if (RocketChat.controller) {
|
// const { signal } = RocketChat.controller;
|
||||||
// @ts-ignore
|
// customOptions = { ...customOptions, signal };
|
||||||
const { signal } = RocketChat.controller;
|
// }
|
||||||
customOptions = { ...customOptions, signal };
|
|
||||||
}
|
|
||||||
return fetch(url, customOptions);
|
return fetch(url, customOptions);
|
||||||
};
|
};
|
||||||
|
|
|
@ -142,13 +142,13 @@ export default {
|
||||||
SE_CONTACT_US: 'se_contact_us',
|
SE_CONTACT_US: 'se_contact_us',
|
||||||
SE_CONTACT_US_F: 'se_contact_us_f',
|
SE_CONTACT_US_F: 'se_contact_us_f',
|
||||||
SE_GO_LANGUAGE: 'se_go_language',
|
SE_GO_LANGUAGE: 'se_go_language',
|
||||||
SE_REVIEW_THIS_APP: 'se_review_this_app',
|
|
||||||
SE_REVIEW_THIS_APP_F: 'se_review_this_app_f',
|
|
||||||
SE_SHARE_THIS_APP: 'se_share_this_app',
|
|
||||||
SE_GO_DEFAULTBROWSER: 'se_go_default_browser',
|
SE_GO_DEFAULTBROWSER: 'se_go_default_browser',
|
||||||
SE_GO_THEME: 'se_go_theme',
|
SE_GO_THEME: 'se_go_theme',
|
||||||
SE_GO_PROFILE: 'se_go_profile',
|
SE_GO_PROFILE: 'se_go_profile',
|
||||||
SE_GO_SECURITYPRIVACY: 'se_go_securityprivacy',
|
SE_GO_SECURITYPRIVACY: 'se_go_securityprivacy',
|
||||||
|
SE_REVIEW_THIS_APP: 'se_review_this_app',
|
||||||
|
SE_REVIEW_THIS_APP_F: 'se_review_this_app_f',
|
||||||
|
SE_SHARE_THIS_APP: 'se_share_this_app',
|
||||||
SE_READ_LICENSE: 'se_read_license',
|
SE_READ_LICENSE: 'se_read_license',
|
||||||
SE_COPY_APP_VERSION: 'se_copy_app_version',
|
SE_COPY_APP_VERSION: 'se_copy_app_version',
|
||||||
SE_COPY_SERVER_VERSION: 'se_copy_server_version',
|
SE_COPY_SERVER_VERSION: 'se_copy_server_version',
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import { IAttachment } from '../views/ShareView/interfaces';
|
import { IShareAttachment } from '../definitions';
|
||||||
|
|
||||||
export const canUploadFile = (
|
export const canUploadFile = ({
|
||||||
file: IAttachment,
|
file,
|
||||||
allowList: string,
|
allowList,
|
||||||
maxFileSize: number,
|
maxFileSize,
|
||||||
permissionToUploadFile: boolean
|
permissionToUploadFile
|
||||||
): { success: boolean; error?: string } => {
|
}: {
|
||||||
|
file: IShareAttachment;
|
||||||
|
allowList?: string;
|
||||||
|
maxFileSize?: number;
|
||||||
|
permissionToUploadFile: boolean;
|
||||||
|
}): { success: boolean; error?: string } => {
|
||||||
if (!(file && file.path)) {
|
if (!(file && file.path)) {
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
if (maxFileSize > -1 && file.size > maxFileSize) {
|
if (maxFileSize && maxFileSize > -1 && file.size > maxFileSize) {
|
||||||
return { success: false, error: 'error-file-too-large' };
|
return { success: false, error: 'error-file-too-large' };
|
||||||
}
|
}
|
||||||
if (!permissionToUploadFile) {
|
if (!permissionToUploadFile) {
|
||||||
|
|
|
@ -29,8 +29,7 @@ const RCSSLPinning = Platform.select({
|
||||||
pickCertificate: () =>
|
pickCertificate: () =>
|
||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const res = await DocumentPicker.pick({
|
const res = await DocumentPicker.pickSingle({
|
||||||
// @ts-ignore
|
|
||||||
type: ['com.rsa.pkcs-12']
|
type: ['com.rsa.pkcs-12']
|
||||||
});
|
});
|
||||||
const { uri, name } = res;
|
const { uri, name } = res;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { RouteProp } from '@react-navigation/native';
|
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||||
import { connect } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { CompositeNavigationProp } from '@react-navigation/core';
|
import { CompositeNavigationProp } from '@react-navigation/core';
|
||||||
|
|
||||||
import * as List from '../containers/List';
|
import * as List from '../containers/List';
|
||||||
|
@ -10,15 +10,14 @@ import * as HeaderButton from '../containers/HeaderButton';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { ChatsStackParamList, DrawerParamList, NewMessageStackParamList } from '../stacks/types';
|
import { ChatsStackParamList, DrawerParamList, NewMessageStackParamList } from '../stacks/types';
|
||||||
|
import { IApplicationState } from '../definitions';
|
||||||
|
|
||||||
interface IAddChannelTeamView {
|
type TRoute = RouteProp<ChatsStackParamList, 'AddChannelTeamView'>;
|
||||||
navigation: CompositeNavigationProp<
|
|
||||||
|
type TNavigation = CompositeNavigationProp<
|
||||||
StackNavigationProp<ChatsStackParamList, 'AddChannelTeamView'>,
|
StackNavigationProp<ChatsStackParamList, 'AddChannelTeamView'>,
|
||||||
CompositeNavigationProp<StackNavigationProp<NewMessageStackParamList>, StackNavigationProp<DrawerParamList>>
|
CompositeNavigationProp<StackNavigationProp<NewMessageStackParamList>, StackNavigationProp<DrawerParamList>>
|
||||||
>;
|
>;
|
||||||
route: RouteProp<ChatsStackParamList, 'AddChannelTeamView'>;
|
|
||||||
isMasterDetail: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const setHeader = ({
|
const setHeader = ({
|
||||||
navigation,
|
navigation,
|
||||||
|
@ -38,12 +37,14 @@ const setHeader = ({
|
||||||
navigation.setOptions(options);
|
navigation.setOptions(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTeamView) => {
|
const AddChannelTeamView = () => {
|
||||||
const { teamId, teamChannels } = route.params;
|
const navigation = useNavigation<TNavigation>();
|
||||||
|
const isMasterDetail = useSelector((state: IApplicationState) => state.app.isMasterDetail);
|
||||||
|
const { teamChannels, teamId } = useRoute<TRoute>().params;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setHeader({ navigation, isMasterDetail });
|
setHeader({ navigation, isMasterDetail });
|
||||||
}, []);
|
}, [isMasterDetail, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView testID='add-channel-team-view'>
|
<SafeAreaView testID='add-channel-team-view'>
|
||||||
|
@ -80,8 +81,4 @@ const AddChannelTeamView = ({ navigation, route, isMasterDetail }: IAddChannelTe
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
export default AddChannelTeamView;
|
||||||
isMasterDetail: state.app.isMasterDetail
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(AddChannelTeamView);
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { goRoom } from '../utils/goRoom';
|
||||||
import { showErrorAlert } from '../utils/info';
|
import { showErrorAlert } from '../utils/info';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import { TSubscriptionModel, SubscriptionType } from '../definitions';
|
import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions';
|
||||||
import { getRoomTitle, hasPermission } from '../lib/methods';
|
import { getRoomTitle, hasPermission } from '../lib/methods';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ interface IAddExistingChannelViewProps {
|
||||||
route: RouteProp<ChatsStackParamList, 'AddExistingChannelView'>;
|
route: RouteProp<ChatsStackParamList, 'AddExistingChannelView'>;
|
||||||
theme: TSupportedThemes;
|
theme: TSupportedThemes;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
addTeamChannelPermission: string[];
|
addTeamChannelPermission?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUERY_SIZE = 50;
|
const QUERY_SIZE = 50;
|
||||||
|
@ -222,7 +222,7 @@ class AddExistingChannelView extends React.Component<IAddExistingChannelViewProp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
addTeamChannelPermission: state.permissions['add-team-channel']
|
addTeamChannelPermission: state.permissions['add-team-channel']
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,38 +1,34 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { WebView } from 'react-native-webview';
|
import { WebView } from 'react-native-webview';
|
||||||
import { connect } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { DrawerScreenProps } from '@react-navigation/drawer';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import { withTheme } from '../../theme';
|
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { AdminPanelStackParamList } from '../../stacks/types';
|
import { AdminPanelStackParamList } from '../../stacks/types';
|
||||||
|
import { IApplicationState } from '../../definitions';
|
||||||
|
|
||||||
interface IAdminPanelViewProps {
|
const AdminPanelView = () => {
|
||||||
baseUrl: string;
|
const navigation = useNavigation<StackNavigationProp<AdminPanelStackParamList, 'AdminPanelView'>>();
|
||||||
token: string;
|
const baseUrl = useSelector((state: IApplicationState) => state.server.server);
|
||||||
}
|
const token = useSelector((state: IApplicationState) => getUserSelector(state).token);
|
||||||
|
const isMasterDetail = useSelector((state: IApplicationState) => state.app.isMasterDetail);
|
||||||
|
|
||||||
interface INavigationOptions {
|
useEffect(() => {
|
||||||
navigation: DrawerScreenProps<AdminPanelStackParamList, 'AdminPanelView'>;
|
navigation.setOptions({
|
||||||
isMasterDetail: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class AdminPanelView extends React.Component<IAdminPanelViewProps, any> {
|
|
||||||
static navigationOptions = ({ navigation, isMasterDetail }: INavigationOptions): StackNavigationOptions => ({
|
|
||||||
headerLeft: isMasterDetail ? undefined : () => <HeaderButton.Drawer navigation={navigation} />,
|
headerLeft: isMasterDetail ? undefined : () => <HeaderButton.Drawer navigation={navigation} />,
|
||||||
title: I18n.t('Admin_Panel')
|
title: I18n.t('Admin_Panel')
|
||||||
});
|
});
|
||||||
|
}, [isMasterDetail, navigation]);
|
||||||
|
|
||||||
render() {
|
|
||||||
const { baseUrl, token } = this.props;
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
|
@ -44,12 +40,6 @@ class AdminPanelView extends React.Component<IAdminPanelViewProps, any> {
|
||||||
/>
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
export default AdminPanelView;
|
||||||
baseUrl: state.server.server,
|
|
||||||
token: getUserSelector(state).token
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(AdminPanelView));
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import StatusBar from '../containers/StatusBar';
|
||||||
import { InsideStackParamList } from '../stacks/types';
|
import { InsideStackParamList } from '../stacks/types';
|
||||||
import { IAttachment } from '../definitions/IAttachment';
|
import { IAttachment } from '../definitions/IAttachment';
|
||||||
import { formatAttachmentUrl } from '../lib/methods/helpers/formatAttachmentUrl';
|
import { formatAttachmentUrl } from '../lib/methods/helpers/formatAttachmentUrl';
|
||||||
|
import { IApplicationState, IUser } from '../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -46,10 +47,7 @@ interface IAttachmentViewProps {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
insets: { left: number; bottom: number; right: number; top: number };
|
insets: { left: number; bottom: number; right: number; top: number };
|
||||||
user: {
|
user: IUser;
|
||||||
id: string;
|
|
||||||
token: string;
|
|
||||||
};
|
|
||||||
Allow_Save_Media_to_Gallery: boolean;
|
Allow_Save_Media_to_Gallery: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,10 +198,10 @@ class AttachmentView extends React.Component<IAttachmentViewProps, IAttachmentVi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
Allow_Save_Media_to_Gallery: state.settings.Allow_Save_Media_to_Gallery ?? true
|
Allow_Save_Media_to_Gallery: (state.settings.Allow_Save_Media_to_Gallery as boolean) ?? true
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(withDimensions(withSafeAreaInsets(AttachmentView))));
|
export default connect(mapStateToProps)(withTheme(withDimensions(withSafeAreaInsets(AttachmentView))));
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { IApplicationState } from '../definitions';
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
|
import { useAppSelector } from '../lib/hooks';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -23,7 +22,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
const AuthLoadingView = React.memo((): React.ReactElement => {
|
const AuthLoadingView = React.memo((): React.ReactElement => {
|
||||||
const text = useSelector((state: IApplicationState) => state.app.text);
|
const text = useAppSelector(state => state.app.text);
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: colors.backgroundColor }]}>
|
<View style={[styles.container, { backgroundColor: colors.backgroundColor }]}>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { TSupportedThemes, withTheme } from '../theme';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
import * as HeaderButton from '../containers/HeaderButton';
|
import * as HeaderButton from '../containers/HeaderButton';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
import { IApplicationState, ICredentials } from '../definitions';
|
||||||
|
|
||||||
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'
|
||||||
|
@ -93,7 +94,7 @@ class AuthenticationWebView extends React.PureComponent<IAuthenticationWebView,
|
||||||
navigation.pop();
|
navigation.pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
login = (params: any) => {
|
login = (params: ICredentials) => {
|
||||||
const { logging } = this.state;
|
const { logging } = this.state;
|
||||||
if (logging) {
|
if (logging) {
|
||||||
return;
|
return;
|
||||||
|
@ -111,7 +112,7 @@ class AuthenticationWebView extends React.PureComponent<IAuthenticationWebView,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Force 3s delay so the server has time to evaluate the token
|
// Force 3s delay so the server has time to evaluate the token
|
||||||
debouncedLogin = debounce((params: any) => this.login(params), 3000);
|
debouncedLogin = debounce((params: ICredentials) => this.login(params), 3000);
|
||||||
|
|
||||||
tryLogin = debounce(
|
tryLogin = debounce(
|
||||||
async () => {
|
async () => {
|
||||||
|
@ -135,7 +136,7 @@ class AuthenticationWebView extends React.PureComponent<IAuthenticationWebView,
|
||||||
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: ICredentials;
|
||||||
if (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 };
|
||||||
|
@ -202,10 +203,10 @@ class AuthenticationWebView extends React.PureComponent<IAuthenticationWebView,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url,
|
Accounts_Iframe_api_url: state.settings.Accounts_Iframe_api_url as string,
|
||||||
Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method
|
Accounts_Iframe_api_method: state.settings.Accounts_Iframe_api_method as string
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
export default connect(mapStateToProps)(withTheme(AuthenticationWebView));
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, { useEffect } from 'react';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { RouteProp } from '@react-navigation/native';
|
import { RouteProp } from '@react-navigation/native';
|
||||||
import { StyleSheet, Text, View, ScrollView } from 'react-native';
|
import { StyleSheet, Text, View, ScrollView } from 'react-native';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
|
@ -17,6 +16,7 @@ import { ICannedResponse } from '../definitions/ICannedResponse';
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import { getRoomTitle, getUidDirectMessage } from '../lib/methods';
|
import { getRoomTitle, getUidDirectMessage } from '../lib/methods';
|
||||||
|
import { useAppSelector } from '../lib/hooks';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scroll: {
|
scroll: {
|
||||||
|
@ -96,8 +96,8 @@ interface ICannedResponseDetailProps {
|
||||||
const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps): JSX.Element => {
|
const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps): JSX.Element => {
|
||||||
const { cannedResponse } = route?.params;
|
const { cannedResponse } = route?.params;
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { isMasterDetail } = useSelector((state: any) => state.app);
|
const { isMasterDetail } = useAppSelector(state => state.app);
|
||||||
const { rooms } = useSelector((state: any) => state.room);
|
const { rooms } = useAppSelector(state => state.room);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
|
@ -159,13 +159,7 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps)
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Button
|
<Button title={I18n.t('Use')} style={styles.button} type='primary' onPress={() => navigateToRoom(cannedResponse)} />
|
||||||
title={I18n.t('Use')}
|
|
||||||
theme={theme}
|
|
||||||
style={styles.button}
|
|
||||||
type='primary'
|
|
||||||
onPress={() => navigateToRoom(cannedResponse)}
|
|
||||||
/>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -40,7 +40,6 @@ const CannedResponseItem = ({
|
||||||
fontSize={12}
|
fontSize={12}
|
||||||
color={themes[theme].titleText}
|
color={themes[theme].titleText}
|
||||||
style={[styles.cannedUseButton, { backgroundColor: themes[theme].chatComponentBackground }]}
|
style={[styles.cannedUseButton, { backgroundColor: themes[theme].chatComponentBackground }]}
|
||||||
theme={theme}
|
|
||||||
onPress={onPressUse}
|
onPress={onPressUse}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useState, useCallback } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList } from 'react-native';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { RouteProp } from '@react-navigation/native';
|
import { RouteProp } from '@react-navigation/native';
|
||||||
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
@ -30,8 +29,8 @@ import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { ISubscription } from '../../definitions/ISubscription';
|
import { ISubscription } from '../../definitions/ISubscription';
|
||||||
import { getRoomTitle, getUidDirectMessage } from '../../lib/methods';
|
import { getRoomTitle, getUidDirectMessage } from '../../lib/methods';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import { IApplicationState } from '../../definitions';
|
|
||||||
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
|
import { ILivechatDepartment } from '../../definitions/ILivechatDepartment';
|
||||||
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
|
||||||
const COUNT = 25;
|
const COUNT = 25;
|
||||||
|
|
||||||
|
@ -76,8 +75,8 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
|
||||||
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isMasterDetail = useSelector((state: IApplicationState) => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
const rooms = useSelector((state: IApplicationState) => state.room.rooms);
|
const rooms = useAppSelector(state => state.room.rooms);
|
||||||
|
|
||||||
const getRoomFromDb = async () => {
|
const getRoomFromDb = async () => {
|
||||||
const { rid } = route.params;
|
const { rid } = route.params;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { FlatList, ScrollView, StyleSheet, Switch, Text, View, SwitchProps } fro
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
|
||||||
import * as List from '../containers/List';
|
import * as List from '../containers/List';
|
||||||
import TextInput from '../presentation/TextInput';
|
import TextInput from '../containers/TextInput';
|
||||||
import Loading from '../containers/Loading';
|
import Loading from '../containers/Loading';
|
||||||
import { createChannelRequest } from '../actions/createChannel';
|
import { createChannelRequest } from '../actions/createChannel';
|
||||||
import { removeUser } from '../actions/selectedUsers';
|
import { removeUser } from '../actions/selectedUsers';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import TextInput from '../../containers/TextInput';
|
import FormTextInput from '../../containers/TextInput/FormTextInput';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion';
|
import { createDiscussionRequest, ICreateDiscussionRequestData } from '../../actions/createDiscussion';
|
||||||
import { showErrorAlert } from '../../utils/info';
|
import { showErrorAlert } from '../../utils/info';
|
||||||
|
@ -167,7 +167,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
|
||||||
serverVersion={serverVersion}
|
serverVersion={serverVersion}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Discussion_name')}
|
label={I18n.t('Discussion_name')}
|
||||||
testID='multi-select-discussion-name'
|
testID='multi-select-discussion-name'
|
||||||
placeholder={I18n.t('A_meaningful_name_for_the_discussion_room')}
|
placeholder={I18n.t('A_meaningful_name_for_the_discussion_room')}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { FlatList, StyleSheet } from 'react-native';
|
import { FlatList, StyleSheet } from 'react-native';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { RouteProp } from '@react-navigation/core';
|
import { RouteProp } from '@react-navigation/core';
|
||||||
|
|
||||||
import { IApplicationState, IMessageFromServer } from '../../definitions';
|
import { IMessageFromServer } from '../../definitions';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import ActivityIndicator from '../../containers/ActivityIndicator';
|
import ActivityIndicator from '../../containers/ActivityIndicator';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -23,6 +22,7 @@ import SearchHeader from '../../containers/SearchHeader';
|
||||||
import { TThreadModel } from '../../definitions/IThread';
|
import { TThreadModel } from '../../definitions/IThread';
|
||||||
import Item from './Item';
|
import Item from './Item';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
|
||||||
const API_FETCH_COUNT = 50;
|
const API_FETCH_COUNT = 50;
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ const DiscussionsView = ({ navigation, route }: IDiscussionsViewProps): React.Re
|
||||||
const rid = route.params?.rid;
|
const rid = route.params?.rid;
|
||||||
const t = route.params?.t;
|
const t = route.params?.t;
|
||||||
|
|
||||||
const baseUrl = useSelector((state: IApplicationState) => state.server?.server);
|
const baseUrl = useAppSelector(state => state.server?.server);
|
||||||
const isMasterDetail = useSelector((state: IApplicationState) => state.app?.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app?.isMasterDetail);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [discussions, setDiscussions] = useState<IMessageFromServer[]>([]);
|
const [discussions, setDiscussions] = useState<IMessageFromServer[]>([]);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { Switch } from 'react-native';
|
import { Switch } from 'react-native';
|
||||||
import { RadioButton } from 'react-native-ui-lib';
|
import { RadioButton } from 'react-native-ui-lib';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { setPreference } from '../actions/sortPreferences';
|
import { setPreference } from '../actions/sortPreferences';
|
||||||
import { DisplayMode, SortBy } from '../lib/constants';
|
import { DisplayMode, SortBy } from '../lib/constants';
|
||||||
|
@ -12,21 +12,22 @@ import * as List from '../containers/List';
|
||||||
import { ICON_SIZE } from '../containers/List/constants';
|
import { ICON_SIZE } from '../containers/List/constants';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import { IApplicationState, IPreferences } from '../definitions';
|
import { IPreferences } from '../definitions';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { SettingsStackParamList } from '../stacks/types';
|
import { SettingsStackParamList } from '../stacks/types';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { events, logEvent } from '../utils/log';
|
import { events, logEvent } from '../utils/log';
|
||||||
import { saveSortPreference } from '../lib/methods';
|
import { saveSortPreference } from '../lib/methods';
|
||||||
|
import { useAppSelector } from '../lib/hooks';
|
||||||
|
|
||||||
const DisplayPrefsView = (): React.ReactElement => {
|
const DisplayPrefsView = (): React.ReactElement => {
|
||||||
const navigation = useNavigation<StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>>();
|
const navigation = useNavigation<StackNavigationProp<SettingsStackParamList, 'DisplayPrefsView'>>();
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
const { sortBy, groupByType, showFavorites, showUnread, showAvatar, displayMode } = useSelector(
|
const { sortBy, groupByType, showFavorites, showUnread, showAvatar, displayMode } = useAppSelector(
|
||||||
(state: IApplicationState) => state.sortPreferences
|
state => state.sortPreferences
|
||||||
);
|
);
|
||||||
const { isMasterDetail } = useSelector((state: IApplicationState) => state.app);
|
const { isMasterDetail } = useAppSelector(state => state.app);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View, TextInput as TextInputComp } from 'react-native';
|
import { StyleSheet, Text, View, TextInput as RNTextInput } from 'react-native';
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
import { StackNavigationOptions } from '@react-navigation/stack';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import * as List from '../containers/List';
|
import * as List from '../containers/List';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import log, { events, logEvent } from '../utils/log';
|
import log, { events, logEvent } from '../utils/log';
|
||||||
import { TSupportedThemes, withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import TextInput from '../containers/TextInput';
|
import FormTextInput from '../containers/TextInput/FormTextInput';
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import { getUserSelector } from '../selectors/login';
|
import { getUserSelector } from '../selectors/login';
|
||||||
import { PADDING_HORIZONTAL } from '../containers/List/constants';
|
import { PADDING_HORIZONTAL } from '../containers/List/constants';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import { Encryption } from '../lib/encryption';
|
import { Encryption } from '../lib/encryption';
|
||||||
import { logout as logoutAction } from '../actions/login';
|
import { logout } from '../actions/login';
|
||||||
import { showConfirmationAlert, showErrorAlert } from '../utils/info';
|
import { showConfirmationAlert, showErrorAlert } from '../utils/info';
|
||||||
import EventEmitter from '../utils/events';
|
import EventEmitter from '../utils/events';
|
||||||
import { LISTENER } from '../containers/Toast';
|
import { LISTENER } from '../containers/Toast';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import { IUser } from '../definitions';
|
import { IApplicationState, IBaseScreen, IUser } from '../definitions';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
import { SettingsStackParamList } from '../stacks/types';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -47,12 +47,10 @@ interface IE2EEncryptionSecurityViewState {
|
||||||
newPassword: string;
|
newPassword: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IE2EEncryptionSecurityViewProps {
|
interface IE2EEncryptionSecurityViewProps extends IBaseScreen<SettingsStackParamList, 'E2EEncryptionSecurityView'> {
|
||||||
theme?: TSupportedThemes;
|
|
||||||
user: IUser;
|
user: IUser;
|
||||||
server: string;
|
server: string;
|
||||||
encryptionEnabled: boolean;
|
encryptionEnabled: boolean;
|
||||||
logout(): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityViewProps, IE2EEncryptionSecurityViewState> {
|
class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityViewProps, IE2EEncryptionSecurityViewState> {
|
||||||
|
@ -66,7 +64,7 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
|
|
||||||
onChangePasswordText = debounce((text: string) => this.setState({ newPassword: text }), 300);
|
onChangePasswordText = debounce((text: string) => this.setState({ newPassword: text }), 300);
|
||||||
|
|
||||||
setNewPasswordRef = (ref: TextInputComp) => (this.newPasswordInputRef = ref);
|
setNewPasswordRef = (ref: RNTextInput) => (this.newPasswordInputRef = ref);
|
||||||
|
|
||||||
changePassword = () => {
|
changePassword = () => {
|
||||||
const { newPassword } = this.state;
|
const { newPassword } = this.state;
|
||||||
|
@ -107,8 +105,8 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
* that's why we're using strict equality to boolean
|
* that's why we're using strict equality to boolean
|
||||||
*/
|
*/
|
||||||
if (res === true) {
|
if (res === true) {
|
||||||
const { logout } = this.props;
|
const { dispatch } = this.props;
|
||||||
logout();
|
dispatch(logout());
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -127,13 +125,13 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<List.Section>
|
<List.Section>
|
||||||
<Text style={[styles.title, { color: themes[theme!].headerTitleColor }]}>
|
<Text style={[styles.title, { color: themes[theme].headerTitleColor }]}>
|
||||||
{I18n.t('E2E_encryption_change_password_title')}
|
{I18n.t('E2E_encryption_change_password_title')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.description, { color: themes[theme!].bodyText }]}>
|
<Text style={[styles.description, { color: themes[theme].bodyText }]}>
|
||||||
{I18n.t('E2E_encryption_change_password_description')}
|
{I18n.t('E2E_encryption_change_password_description')}
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
inputRef={this.setNewPasswordRef}
|
inputRef={this.setNewPasswordRef}
|
||||||
placeholder={I18n.t('New_Password')}
|
placeholder={I18n.t('New_Password')}
|
||||||
returnKeyType='send'
|
returnKeyType='send'
|
||||||
|
@ -146,7 +144,6 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
<Button
|
<Button
|
||||||
onPress={this.changePassword}
|
onPress={this.changePassword}
|
||||||
title={I18n.t('Save_Changes')}
|
title={I18n.t('Save_Changes')}
|
||||||
theme={theme}
|
|
||||||
disabled={!newPassword.trim()}
|
disabled={!newPassword.trim()}
|
||||||
style={styles.changePasswordButton}
|
style={styles.changePasswordButton}
|
||||||
testID='e2e-encryption-security-view-change-password'
|
testID='e2e-encryption-security-view-change-password'
|
||||||
|
@ -161,25 +158,24 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<SafeAreaView testID='e2e-encryption-security-view' style={{ backgroundColor: themes[theme!].backgroundColor }}>
|
<SafeAreaView testID='e2e-encryption-security-view' style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<List.Container>
|
<List.Container>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{this.renderChangePassword()}
|
{this.renderChangePassword()}
|
||||||
|
|
||||||
<List.Section>
|
<List.Section>
|
||||||
<Text style={[styles.title, { color: themes[theme!].headerTitleColor }]}>
|
<Text style={[styles.title, { color: themes[theme].headerTitleColor }]}>
|
||||||
{I18n.t('E2E_encryption_reset_title')}
|
{I18n.t('E2E_encryption_reset_title')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.description, { color: themes[theme!].bodyText }]}>
|
<Text style={[styles.description, { color: themes[theme].bodyText }]}>
|
||||||
{I18n.t('E2E_encryption_reset_description')}
|
{I18n.t('E2E_encryption_reset_description')}
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
onPress={this.resetOwnKey}
|
onPress={this.resetOwnKey}
|
||||||
title={I18n.t('E2E_encryption_reset_button')}
|
title={I18n.t('E2E_encryption_reset_button')}
|
||||||
theme={theme}
|
|
||||||
type='secondary'
|
type='secondary'
|
||||||
backgroundColor={themes[theme!].chatComponentBackground}
|
backgroundColor={themes[theme].chatComponentBackground}
|
||||||
testID='e2e-encryption-security-view-reset-key'
|
testID='e2e-encryption-security-view-reset-key'
|
||||||
/>
|
/>
|
||||||
</List.Section>
|
</List.Section>
|
||||||
|
@ -190,14 +186,10 @@ class E2EEncryptionSecurityView extends React.Component<IE2EEncryptionSecurityVi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
encryptionEnabled: state.encryption.enabled
|
encryptionEnabled: state.encryption.enabled
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
export default connect(mapStateToProps)(withTheme(E2EEncryptionSecurityView));
|
||||||
logout: () => dispatch(logoutAction(true))
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(E2EEncryptionSecurityView));
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Button from '../containers/Button';
|
||||||
import * as HeaderButton from '../containers/HeaderButton';
|
import * as HeaderButton from '../containers/HeaderButton';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import TextInput from '../containers/TextInput';
|
import FormTextInput from '../containers/TextInput/FormTextInput';
|
||||||
import { IBaseScreen } from '../definitions';
|
import { IBaseScreen } from '../definitions';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import KeyboardView from '../containers/KeyboardView';
|
import KeyboardView from '../containers/KeyboardView';
|
||||||
|
@ -75,7 +75,7 @@ class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordView
|
||||||
<SafeAreaView
|
<SafeAreaView
|
||||||
style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}
|
style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}
|
||||||
testID='e2e-enter-your-password-view'>
|
testID='e2e-enter-your-password-view'>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
inputRef={(e: RNTextInput) => {
|
inputRef={(e: RNTextInput) => {
|
||||||
this.passwordInput = e;
|
this.passwordInput = e;
|
||||||
}}
|
}}
|
||||||
|
@ -93,7 +93,6 @@ class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordView
|
||||||
onPress={this.submit}
|
onPress={this.submit}
|
||||||
title={I18n.t('Confirm')}
|
title={I18n.t('Confirm')}
|
||||||
disabled={!password}
|
disabled={!password}
|
||||||
theme={theme}
|
|
||||||
testID='e2e-enter-your-password-view-confirm'
|
testID='e2e-enter-your-password-view-confirm'
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Enter_Your_Encryption_Password_desc1')}</Text>
|
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Enter_Your_Encryption_Password_desc1')}</Text>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
|
||||||
import { RouteProp } from '@react-navigation/native';
|
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import * as HeaderButton from '../containers/HeaderButton';
|
import * as HeaderButton from '../containers/HeaderButton';
|
||||||
import Markdown from '../containers/markdown';
|
import Markdown from '../containers/markdown';
|
||||||
import { TSupportedThemes, withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
|
import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
|
||||||
|
import { IBaseScreen } from '../definitions';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -23,17 +22,10 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface INavigation {
|
type TE2EHowItWorksViewProps = IBaseScreen<E2ESaveYourPasswordStackParamList, 'E2EHowItWorksView'>;
|
||||||
navigation: StackNavigationProp<E2ESaveYourPasswordStackParamList, 'E2EHowItWorksView'>;
|
|
||||||
route: RouteProp<E2ESaveYourPasswordStackParamList, 'E2EHowItWorksView'>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IE2EHowItWorksViewProps extends INavigation {
|
class E2EHowItWorksView extends React.Component<TE2EHowItWorksViewProps, any> {
|
||||||
theme: TSupportedThemes;
|
static navigationOptions = ({ route, navigation }: Pick<TE2EHowItWorksViewProps, 'navigation' | 'route'>) => {
|
||||||
}
|
|
||||||
|
|
||||||
class E2EHowItWorksView extends React.Component<IE2EHowItWorksViewProps, any> {
|
|
||||||
static navigationOptions = ({ route, navigation }: INavigation) => {
|
|
||||||
const showCloseModal = route.params?.showCloseModal;
|
const showCloseModal = route.params?.showCloseModal;
|
||||||
return {
|
return {
|
||||||
title: I18n.t('How_It_Works'),
|
title: I18n.t('How_It_Works'),
|
||||||
|
|
|
@ -55,7 +55,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IE2ESaveYourPasswordViewState {
|
interface IE2ESaveYourPasswordViewState {
|
||||||
password: string;
|
password: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IE2ESaveYourPasswordViewProps extends IBaseScreen<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'> {
|
interface IE2ESaveYourPasswordViewProps extends IBaseScreen<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'> {
|
||||||
|
@ -87,7 +87,7 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
||||||
// Set stored password on local state
|
// Set stored password on local state
|
||||||
const password = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
const password = UserPreferences.getString(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ password: password! });
|
this.setState({ password });
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.state.password = password;
|
this.state.password = password;
|
||||||
|
@ -110,8 +110,10 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
||||||
onCopy = () => {
|
onCopy = () => {
|
||||||
logEvent(events.E2E_SAVE_PW_COPY);
|
logEvent(events.E2E_SAVE_PW_COPY);
|
||||||
const { password } = this.state;
|
const { password } = this.state;
|
||||||
|
if (password) {
|
||||||
Clipboard.setString(password);
|
Clipboard.setString(password);
|
||||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onHowItWorks = () => {
|
onHowItWorks = () => {
|
||||||
|
@ -144,7 +146,6 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
||||||
title={I18n.t('Copy')}
|
title={I18n.t('Copy')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
fontSize={12}
|
fontSize={12}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Save_Your_Encryption_Password_info')}</Text>
|
<Text style={[styles.info, { color: themes[theme].bodyText }]}>{I18n.t('Save_Your_Encryption_Password_info')}</Text>
|
||||||
|
@ -153,13 +154,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
|
||||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
||||||
title={I18n.t('How_It_Works')}
|
title={I18n.t('How_It_Works')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
theme={theme}
|
|
||||||
testID='e2e-save-password-view-how-it-works'
|
testID='e2e-save-password-view-how-it-works'
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onPress={this.onSaved}
|
onPress={this.onSaved}
|
||||||
title={I18n.t('I_Saved_My_E2E_Password')}
|
title={I18n.t('I_Saved_My_E2E_Password')}
|
||||||
theme={theme}
|
|
||||||
testID='e2e-save-password-view-saved-password'
|
testID='e2e-save-password-view-saved-password'
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Text } from 'react-native';
|
||||||
|
|
||||||
import Button from '../containers/Button';
|
import Button from '../containers/Button';
|
||||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
import TextInput from '../containers/TextInput';
|
import FormTextInput from '../containers/TextInput/FormTextInput';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
@ -92,7 +92,7 @@ class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForg
|
||||||
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>
|
<Text style={[sharedStyles.loginTitle, sharedStyles.textBold, { color: themes[theme].titleText }]}>
|
||||||
{I18n.t('Forgot_password')}
|
{I18n.t('Forgot_password')}
|
||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder={I18n.t('Email')}
|
placeholder={I18n.t('Email')}
|
||||||
keyboardType='email-address'
|
keyboardType='email-address'
|
||||||
|
@ -110,7 +110,6 @@ class ForgotPasswordView extends React.Component<IForgotPasswordViewProps, IForg
|
||||||
testID='forgot-password-view-submit'
|
testID='forgot-password-view-submit'
|
||||||
loading={isFetching}
|
loading={isFetching}
|
||||||
disabled={invalidEmail}
|
disabled={invalidEmail}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
</FormContainerInner>
|
</FormContainerInner>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
|
@ -121,7 +121,6 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { theme } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<List.Container>
|
<List.Container>
|
||||||
|
@ -134,7 +133,7 @@ class InviteUsersEditView extends React.Component<IInviteUsersEditViewProps, any
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
</List.Section>
|
</List.Section>
|
||||||
<View style={styles.innerContainer}>
|
<View style={styles.innerContainer}>
|
||||||
<Button title={I18n.t('Generate_New_Link')} type='primary' onPress={this.createInviteLink} theme={theme} />
|
<Button title={I18n.t('Generate_New_Link')} type='primary' onPress={this.createInviteLink} />
|
||||||
</View>
|
</View>
|
||||||
</List.Container>
|
</List.Container>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
|
|
|
@ -1,69 +1,58 @@
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
import React, { useEffect } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import React from 'react';
|
|
||||||
import { ScrollView, Share, View } from 'react-native';
|
import { ScrollView, Share, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { inviteLinksClear, inviteLinksCreate } from '../../actions/inviteLinks';
|
import { inviteLinksClear, inviteLinksCreate } from '../../actions/inviteLinks';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import Button from '../../containers/Button';
|
import Button from '../../containers/Button';
|
||||||
import Markdown from '../../containers/markdown';
|
import Markdown from '../../containers/markdown';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import RCTextInput from '../../containers/TextInput';
|
import FormTextInput from '../../containers/TextInput/FormTextInput';
|
||||||
import { IApplicationState, IBaseScreen } from '../../definitions';
|
import { IApplicationState, IBaseScreen } from '../../definitions';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { TInvite } from '../../reducers/inviteLinks';
|
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { withTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IInviteUsersViewProps extends IBaseScreen<ChatsStackParamList, 'InviteUsersView'> {
|
type IInviteUsersViewProps = IBaseScreen<ChatsStackParamList, 'InviteUsersView'>;
|
||||||
timeDateFormat: string;
|
|
||||||
invite: TInvite;
|
const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.ReactElement => {
|
||||||
}
|
const rid = route.params?.rid;
|
||||||
class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
|
const timeDateFormat = useSelector((state: IApplicationState) => state.settings.Message_TimeAndDateFormat as string);
|
||||||
static navigationOptions = (): StackNavigationOptions => ({
|
const invite = useSelector((state: IApplicationState) => state.inviteLinks.invite);
|
||||||
|
const { colors, theme } = useTheme();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
title: I18n.t('Invite_users')
|
title: I18n.t('Invite_users')
|
||||||
});
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
private rid: string;
|
useEffect(() => {
|
||||||
|
dispatch(inviteLinksCreate(rid));
|
||||||
constructor(props: IInviteUsersViewProps) {
|
return () => {
|
||||||
super(props);
|
|
||||||
this.rid = props.route.params?.rid;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
dispatch(inviteLinksCreate(this.rid));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
dispatch(inviteLinksClear());
|
dispatch(inviteLinksClear());
|
||||||
}
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
share = () => {
|
const share = () => {
|
||||||
logEvent(events.IU_SHARE);
|
logEvent(events.IU_SHARE);
|
||||||
const { invite } = this.props;
|
|
||||||
if (!invite || !invite.url) {
|
if (!invite || !invite.url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Share.share({ message: invite.url });
|
Share.share({ message: invite.url });
|
||||||
};
|
};
|
||||||
|
|
||||||
edit = () => {
|
const edit = () => {
|
||||||
logEvent(events.IU_GO_IU_EDIT);
|
logEvent(events.IU_GO_IU_EDIT);
|
||||||
const { navigation } = this.props;
|
navigation.navigate('InviteUsersEditView', { rid });
|
||||||
navigation.navigate('InviteUsersEditView', { rid: this.rid });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
linkExpirationText = () => {
|
const linkExpirationText = () => {
|
||||||
const { timeDateFormat, invite } = this.props;
|
|
||||||
|
|
||||||
if (!invite || !invite.url) {
|
if (!invite || !invite.url) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -90,38 +79,28 @@ class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
|
||||||
return I18n.t('Your_invite_link_will_never_expire');
|
return I18n.t('Your_invite_link_will_never_expire');
|
||||||
};
|
};
|
||||||
|
|
||||||
renderExpiration = () => {
|
const renderExpiration = () => {
|
||||||
const { theme } = this.props;
|
const expirationMessage = linkExpirationText();
|
||||||
const expirationMessage = this.linkExpirationText();
|
|
||||||
return <Markdown msg={expirationMessage} theme={theme} />;
|
return <Markdown msg={expirationMessage} theme={theme} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const { theme, invite } = this.props;
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
<SafeAreaView style={{ backgroundColor: colors.backgroundColor }}>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
|
style={{ backgroundColor: colors.auxiliaryBackground }}
|
||||||
showsVerticalScrollIndicator={false}>
|
showsVerticalScrollIndicator={false}>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<View style={styles.innerContainer}>
|
<View style={styles.innerContainer}>
|
||||||
<RCTextInput label={I18n.t('Invite_Link')} theme={theme} value={invite && invite.url} editable={false} />
|
<FormTextInput label={I18n.t('Invite_Link')} theme={theme} value={invite && invite.url} editable={false} />
|
||||||
{this.renderExpiration()}
|
{renderExpiration()}
|
||||||
<View style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
|
<View style={[styles.divider, { backgroundColor: colors.separatorColor }]} />
|
||||||
<Button title={I18n.t('Share_Link')} type='primary' onPress={this.share} theme={theme} />
|
<Button title={I18n.t('Share_Link')} type='primary' onPress={share} />
|
||||||
<Button title={I18n.t('Edit_Invite')} type='secondary' onPress={this.edit} theme={theme} />
|
<Button title={I18n.t('Edit_Invite')} type='secondary' onPress={edit} />
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: IApplicationState) => ({
|
export default InviteUsersView;
|
||||||
timeDateFormat: state.settings.Message_TimeAndDateFormat as string,
|
|
||||||
invite: state.inviteLinks.invite
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(InviteUsersView));
|
|
||||||
|
|
|
@ -1,33 +1,36 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import StatusBar from '../containers/StatusBar';
|
import StatusBar from '../containers/StatusBar';
|
||||||
import openLink from '../utils/openLink';
|
import openLink from '../utils/openLink';
|
||||||
import { TSupportedThemes, withTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import SafeAreaView from '../containers/SafeAreaView';
|
import SafeAreaView from '../containers/SafeAreaView';
|
||||||
import * as List from '../containers/List';
|
import * as List from '../containers/List';
|
||||||
|
import { OutsideParamList } from '../stacks/types';
|
||||||
|
import { IBaseScreen, IApplicationState } from '../definitions';
|
||||||
|
|
||||||
interface ILegalView {
|
interface ILegalViewProps extends IBaseScreen<OutsideParamList, 'LegalView'> {
|
||||||
server: string;
|
server: string;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LegalView extends React.Component<ILegalView, any> {
|
const LegalView = ({ navigation }: ILegalViewProps): React.ReactElement => {
|
||||||
static navigationOptions = (): StackNavigationOptions => ({
|
const server = useSelector((state: IApplicationState) => state.server.server);
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
title: I18n.t('Legal')
|
title: I18n.t('Legal')
|
||||||
});
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
onPressItem = ({ route }: { route: string }) => {
|
const onPressItem = ({ route }: { route: string }) => {
|
||||||
const { server, theme } = this.props;
|
|
||||||
if (!server) {
|
if (!server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openLink(`${server}/${route}`, theme);
|
openLink(`${server}/${route}`, theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView testID='legal-view'>
|
<SafeAreaView testID='legal-view'>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
|
@ -36,14 +39,14 @@ class LegalView extends React.Component<ILegalView, any> {
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
<List.Item
|
<List.Item
|
||||||
title='Terms_of_Service'
|
title='Terms_of_Service'
|
||||||
onPress={() => this.onPressItem({ route: 'terms-of-service' })}
|
onPress={() => onPressItem({ route: 'terms-of-service' })}
|
||||||
testID='legal-terms-button'
|
testID='legal-terms-button'
|
||||||
showActionIndicator
|
showActionIndicator
|
||||||
/>
|
/>
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
<List.Item
|
<List.Item
|
||||||
title='Privacy_Policy'
|
title='Privacy_Policy'
|
||||||
onPress={() => this.onPressItem({ route: 'privacy-policy' })}
|
onPress={() => onPressItem({ route: 'privacy-policy' })}
|
||||||
testID='legal-privacy-button'
|
testID='legal-privacy-button'
|
||||||
showActionIndicator
|
showActionIndicator
|
||||||
/>
|
/>
|
||||||
|
@ -52,11 +55,6 @@ class LegalView extends React.Component<ILegalView, any> {
|
||||||
</List.Container>
|
</List.Container>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
export default LegalView;
|
||||||
server: state.server.server
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(LegalView));
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||||
|
|
||||||
import { TSupportedThemes, withTheme } from '../theme';
|
import { TSupportedThemes, withTheme } from '../theme';
|
||||||
import { themes } from '../lib/constants';
|
import { themes } from '../lib/constants';
|
||||||
import TextInput from '../containers/TextInput';
|
import FormTextInput from '../containers/TextInput/FormTextInput';
|
||||||
import KeyboardView from '../containers/KeyboardView';
|
import KeyboardView from '../containers/KeyboardView';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { LISTENER } from '../containers/Toast';
|
import { LISTENER } from '../containers/Toast';
|
||||||
|
@ -193,7 +193,7 @@ const LivechatEditView = ({
|
||||||
<ScrollView {...scrollPersistTaps} style={styles.container}>
|
<ScrollView {...scrollPersistTaps} style={styles.container}>
|
||||||
<SafeAreaView>
|
<SafeAreaView>
|
||||||
<Title title={visitor?.username} theme={theme} />
|
<Title title={visitor?.username} theme={theme} />
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Name')}
|
label={I18n.t('Name')}
|
||||||
defaultValue={visitor?.name}
|
defaultValue={visitor?.name}
|
||||||
onChangeText={text => onChangeText('name', text)}
|
onChangeText={text => onChangeText('name', text)}
|
||||||
|
@ -203,7 +203,7 @@ const LivechatEditView = ({
|
||||||
theme={theme}
|
theme={theme}
|
||||||
editable={!!permissions[0]}
|
editable={!!permissions[0]}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Email')}
|
label={I18n.t('Email')}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
inputs.name = e;
|
inputs.name = e;
|
||||||
|
@ -216,7 +216,7 @@ const LivechatEditView = ({
|
||||||
theme={theme}
|
theme={theme}
|
||||||
editable={!!permissions[0]}
|
editable={!!permissions[0]}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Phone')}
|
label={I18n.t('Phone')}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
inputs.phone = e;
|
inputs.phone = e;
|
||||||
|
@ -236,7 +236,7 @@ const LivechatEditView = ({
|
||||||
editable={!!permissions[0]}
|
editable={!!permissions[0]}
|
||||||
/>
|
/>
|
||||||
{Object.entries(customFields?.visitor || {}).map(([key, value], index, array) => (
|
{Object.entries(customFields?.visitor || {}).map(([key, value], index, array) => (
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={key}
|
label={key}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
|
@ -254,7 +254,7 @@ const LivechatEditView = ({
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Title title={I18n.t('Conversation')} theme={theme} />
|
<Title title={I18n.t('Conversation')} theme={theme} />
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Topic')}
|
label={I18n.t('Topic')}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
inputs.topic = e;
|
inputs.topic = e;
|
||||||
|
@ -280,7 +280,7 @@ const LivechatEditView = ({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{Object.entries(customFields?.livechat || {}).map(([key, value], index, array: any) => (
|
{Object.entries(customFields?.livechat || {}).map(([key, value], index, array: any) => (
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={key}
|
label={key}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
|
@ -298,7 +298,7 @@ const LivechatEditView = ({
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Button title={I18n.t('Save')} onPress={submit} theme={theme} />
|
<Button title={I18n.t('Save')} onPress={submit} />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Button from '../containers/Button';
|
||||||
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
import FormContainer, { FormContainerInner } from '../containers/FormContainer';
|
||||||
import * as HeaderButton from '../containers/HeaderButton';
|
import * as HeaderButton from '../containers/HeaderButton';
|
||||||
import LoginServices from '../containers/LoginServices';
|
import LoginServices from '../containers/LoginServices';
|
||||||
import TextInput from '../containers/TextInput';
|
import FormTextInput from '../containers/TextInput/FormTextInput';
|
||||||
import { IApplicationState, IBaseScreen } from '../definitions';
|
import { IApplicationState, IBaseScreen } from '../definitions';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { OutsideParamList } from '../stacks/types';
|
import { OutsideParamList } from '../stacks/types';
|
||||||
|
@ -157,7 +157,7 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Login')}</Text>
|
<Text style={[styles.title, sharedStyles.textBold, { color: themes[theme].titleText }]}>{I18n.t('Login')}</Text>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Username_or_email')}
|
label={I18n.t('Username_or_email')}
|
||||||
containerStyle={styles.inputContainer}
|
containerStyle={styles.inputContainer}
|
||||||
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
|
placeholder={Accounts_EmailOrUsernamePlaceholder || I18n.t('Username_or_email')}
|
||||||
|
@ -173,7 +173,7 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
|
||||||
theme={theme}
|
theme={theme}
|
||||||
value={user}
|
value={user}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Password')}
|
label={I18n.t('Password')}
|
||||||
containerStyle={styles.inputContainer}
|
containerStyle={styles.inputContainer}
|
||||||
inputRef={e => {
|
inputRef={e => {
|
||||||
|
@ -196,7 +196,6 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
|
||||||
testID='login-view-submit'
|
testID='login-view-submit'
|
||||||
loading={isFetching}
|
loading={isFetching}
|
||||||
disabled={!this.valid()}
|
disabled={!this.valid()}
|
||||||
theme={theme}
|
|
||||||
style={styles.loginButton}
|
style={styles.loginButton}
|
||||||
/>
|
/>
|
||||||
{Accounts_PasswordReset && (
|
{Accounts_PasswordReset && (
|
||||||
|
@ -205,7 +204,6 @@ class LoginView extends React.Component<ILoginViewProps, ILoginViewState> {
|
||||||
type='secondary'
|
type='secondary'
|
||||||
onPress={this.forgotPassword}
|
onPress={this.forgotPassword}
|
||||||
testID='login-view-forgot-password'
|
testID='login-view-forgot-password'
|
||||||
theme={theme}
|
|
||||||
color={themes[theme].auxiliaryText}
|
color={themes[theme].auxiliaryText}
|
||||||
fontSize={14}
|
fontSize={14}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,43 +1,38 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { ScrollView } from 'react-native';
|
import { ScrollView } from 'react-native';
|
||||||
import { StackNavigationOptions } from '@react-navigation/stack';
|
|
||||||
import { RouteProp } from '@react-navigation/native';
|
|
||||||
|
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
import { isIOS } from '../utils/deviceInfo';
|
import { isIOS } from '../utils/deviceInfo';
|
||||||
import { themes } from '../lib/constants';
|
import { useTheme } from '../theme';
|
||||||
import { TSupportedThemes, withTheme } from '../theme';
|
|
||||||
import { ChatsStackParamList } from '../stacks/types';
|
import { ChatsStackParamList } from '../stacks/types';
|
||||||
|
import { IBaseScreen } from '../definitions';
|
||||||
|
|
||||||
interface IMarkdownTableViewProps {
|
type IMarkdownTableViewProps = IBaseScreen<ChatsStackParamList, 'MarkdownTableView'>;
|
||||||
route: RouteProp<ChatsStackParamList, 'MarkdownTableView'>;
|
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MarkdownTableView extends React.Component<IMarkdownTableViewProps> {
|
const MarkdownTableView = ({ navigation, route }: IMarkdownTableViewProps): React.ReactElement => {
|
||||||
static navigationOptions = (): StackNavigationOptions => ({
|
|
||||||
title: I18n.t('Table')
|
|
||||||
});
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { route, theme } = this.props;
|
|
||||||
const renderRows = route.params?.renderRows;
|
const renderRows = route.params?.renderRows;
|
||||||
const tableWidth = route.params?.tableWidth;
|
const tableWidth = route.params?.tableWidth;
|
||||||
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
title: I18n.t('Table')
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (isIOS) {
|
if (isIOS) {
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }} contentContainerStyle={{ width: tableWidth }}>
|
<ScrollView style={{ backgroundColor: colors.backgroundColor }} contentContainerStyle={{ width: tableWidth }}>
|
||||||
{renderRows()}
|
{renderRows()}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
<ScrollView style={{ backgroundColor: colors.backgroundColor }}>
|
||||||
<ScrollView horizontal>{renderRows()}</ScrollView>
|
<ScrollView horizontal>{renderRows()}</ScrollView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default withTheme(MarkdownTableView);
|
export default MarkdownTableView;
|
||||||
|
|
|
@ -20,7 +20,17 @@ import getThreadName from '../../lib/methods/getThreadName';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { IRoomInfoParam } from '../SearchMessagesView';
|
import { IRoomInfoParam } from '../SearchMessagesView';
|
||||||
import { TMessageModel, IEmoji, ISubscription, SubscriptionType, IUrl } from '../../definitions';
|
import {
|
||||||
|
IApplicationState,
|
||||||
|
TMessageModel,
|
||||||
|
IEmoji,
|
||||||
|
ISubscription,
|
||||||
|
SubscriptionType,
|
||||||
|
IAttachment,
|
||||||
|
IMessage,
|
||||||
|
TAnyMessageModel,
|
||||||
|
IUrl
|
||||||
|
} from '../../definitions';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
|
||||||
interface IMessagesViewProps {
|
interface IMessagesViewProps {
|
||||||
|
@ -37,36 +47,19 @@ interface IMessagesViewProps {
|
||||||
route: RouteProp<ChatsStackParamList, 'MessagesView'>;
|
route: RouteProp<ChatsStackParamList, 'MessagesView'>;
|
||||||
customEmojis: { [key: string]: IEmoji };
|
customEmojis: { [key: string]: IEmoji };
|
||||||
theme: TSupportedThemes;
|
theme: TSupportedThemes;
|
||||||
showActionSheet: Function;
|
showActionSheet: (params: { options: string[]; hasCancel: boolean }) => void;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
isMasterDetail: boolean;
|
isMasterDetail: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessagesViewState {
|
interface IMessagesViewState {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
messages: [];
|
messages: IMessage[];
|
||||||
|
message?: IMessage;
|
||||||
fileLoading: boolean;
|
fileLoading: boolean;
|
||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageItem {
|
|
||||||
u?: string;
|
|
||||||
user?: string;
|
|
||||||
editedAt?: Date;
|
|
||||||
attachments?: any;
|
|
||||||
_id: string;
|
|
||||||
tmid?: string;
|
|
||||||
ts?: Date;
|
|
||||||
uploadedAt?: Date;
|
|
||||||
name?: string;
|
|
||||||
description?: string;
|
|
||||||
msg?: string;
|
|
||||||
starred: boolean;
|
|
||||||
pinned: boolean;
|
|
||||||
type: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IParams {
|
interface IParams {
|
||||||
rid: string;
|
rid: string;
|
||||||
t: SubscriptionType;
|
t: SubscriptionType;
|
||||||
|
@ -75,24 +68,25 @@ interface IParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
fname?: string;
|
fname?: string;
|
||||||
prid?: string;
|
prid?: string;
|
||||||
room: ISubscription;
|
room?: ISubscription;
|
||||||
jumpToMessageId?: string;
|
jumpToMessageId?: string;
|
||||||
jumpToThreadId?: string;
|
jumpToThreadId?: string;
|
||||||
roomUserId?: string;
|
roomUserId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessagesView extends React.Component<IMessagesViewProps, any> {
|
class MessagesView extends React.Component<IMessagesViewProps, IMessagesViewState> {
|
||||||
private rid: string;
|
private rid: string;
|
||||||
private t: SubscriptionType;
|
private t: SubscriptionType;
|
||||||
private content: any;
|
private content: any;
|
||||||
private room: any;
|
private room?: ISubscription;
|
||||||
|
|
||||||
constructor(props: IMessagesViewProps) {
|
constructor(props: IMessagesViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
messages: [],
|
messages: [],
|
||||||
fileLoading: true
|
fileLoading: true,
|
||||||
|
total: 0
|
||||||
};
|
};
|
||||||
this.setHeader();
|
this.setHeader();
|
||||||
this.rid = props.route.params?.rid;
|
this.rid = props.route.params?.rid;
|
||||||
|
@ -104,7 +98,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps: IMessagesViewProps, nextState: any) {
|
shouldComponentUpdate(nextProps: IMessagesViewProps, nextState: IMessagesViewState) {
|
||||||
const { loading, messages, fileLoading } = this.state;
|
const { loading, messages, fileLoading } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
|
@ -137,7 +131,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
navigation.navigate('RoomInfoView', navParam);
|
navigation.navigate('RoomInfoView', navParam);
|
||||||
};
|
};
|
||||||
|
|
||||||
jumpToMessage = async ({ item }: { item: IMessageItem }) => {
|
jumpToMessage = async ({ item }: { item: IMessage }) => {
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
let params: IParams = {
|
let params: IParams = {
|
||||||
rid: this.rid,
|
rid: this.rid,
|
||||||
|
@ -165,7 +159,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
|
|
||||||
defineMessagesViewContent = (name: string) => {
|
defineMessagesViewContent = (name: string) => {
|
||||||
const { user, baseUrl, theme, useRealName } = this.props;
|
const { user, baseUrl, theme, useRealName } = this.props;
|
||||||
const renderItemCommonProps = (item: IMessageItem) => ({
|
const renderItemCommonProps = (item: TAnyMessageModel) => ({
|
||||||
item,
|
item,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
user,
|
user,
|
||||||
|
@ -224,8 +218,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
},
|
},
|
||||||
noDataMsg: I18n.t('No_mentioned_messages'),
|
noDataMsg: I18n.t('No_mentioned_messages'),
|
||||||
testID: 'mentioned-messages-view',
|
testID: 'mentioned-messages-view',
|
||||||
// @ts-ignore TODO: unify IMessage
|
renderItem: (item: TAnyMessageModel) => <Message {...renderItemCommonProps(item)} msg={item.msg} theme={theme} />
|
||||||
renderItem: (item: IMessageItem) => <Message {...renderItemCommonProps(item)} msg={item.msg} theme={theme} />
|
|
||||||
},
|
},
|
||||||
// Starred Messages Screen
|
// Starred Messages Screen
|
||||||
Starred: {
|
Starred: {
|
||||||
|
@ -236,16 +229,15 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
},
|
},
|
||||||
noDataMsg: I18n.t('No_starred_messages'),
|
noDataMsg: I18n.t('No_starred_messages'),
|
||||||
testID: 'starred-messages-view',
|
testID: 'starred-messages-view',
|
||||||
renderItem: (item: IMessageItem) => (
|
renderItem: (item: TAnyMessageModel) => (
|
||||||
// @ts-ignore TODO: unify IMessage
|
|
||||||
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
|
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
|
||||||
),
|
),
|
||||||
action: (message: IMessageItem) => ({
|
action: (message: IMessage) => ({
|
||||||
title: I18n.t('Unstar'),
|
title: I18n.t('Unstar'),
|
||||||
icon: message.starred ? 'star-filled' : 'star',
|
icon: message.starred ? 'star-filled' : 'star',
|
||||||
onPress: this.handleActionPress
|
onPress: this.handleActionPress
|
||||||
}),
|
}),
|
||||||
handleActionPress: (message: IMessageItem) => Services.toggleStarMessage(message._id, message.starred)
|
handleActionPress: (message: IMessage) => Services.toggleStarMessage(message._id, message.starred)
|
||||||
},
|
},
|
||||||
// Pinned Messages Screen
|
// Pinned Messages Screen
|
||||||
Pinned: {
|
Pinned: {
|
||||||
|
@ -256,14 +248,12 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
},
|
},
|
||||||
noDataMsg: I18n.t('No_pinned_messages'),
|
noDataMsg: I18n.t('No_pinned_messages'),
|
||||||
testID: 'pinned-messages-view',
|
testID: 'pinned-messages-view',
|
||||||
renderItem: (item: IMessageItem) => (
|
renderItem: (item: TAnyMessageModel) => (
|
||||||
// @ts-ignore TODO: unify IMessage
|
|
||||||
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
|
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
|
||||||
),
|
),
|
||||||
action: () => ({ title: I18n.t('Unpin'), icon: 'pin', onPress: this.handleActionPress }),
|
action: () => ({ title: I18n.t('Unpin'), icon: 'pin', onPress: this.handleActionPress }),
|
||||||
handleActionPress: (message: IMessageItem) => Services.togglePinMessage(message._id, message.pinned)
|
handleActionPress: (message: IMessage) => Services.togglePinMessage(message._id, message.pinned)
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
|
||||||
}[name];
|
}[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -316,12 +306,12 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
showAttachment = (attachment: any) => {
|
showAttachment = (attachment: IAttachment) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('AttachmentView', { attachment });
|
navigation.navigate('AttachmentView', { attachment });
|
||||||
};
|
};
|
||||||
|
|
||||||
onLongPress = (message: IMessageItem) => {
|
onLongPress = (message: IMessage) => {
|
||||||
this.setState({ message }, this.showActionSheet);
|
this.setState({ message }, this.showActionSheet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -338,7 +328,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
const result = await this.content.handleActionPress(message);
|
const result = await this.content.handleActionPress(message);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.setState((prevState: IMessagesViewState) => ({
|
this.setState((prevState: IMessagesViewState) => ({
|
||||||
messages: prevState.messages.filter((item: IMessageItem) => item._id !== message._id),
|
messages: prevState.messages.filter((item: IMessage) => item._id !== message?._id),
|
||||||
total: prevState.total - 1
|
total: prevState.total - 1
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -360,7 +350,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderItem = ({ item }: { item: IMessageItem }) => this.content.renderItem(item);
|
renderItem = ({ item }: { item: IMessage }) => this.content.renderItem(item);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { messages, loading } = this.state;
|
const { messages, loading } = this.state;
|
||||||
|
@ -386,7 +376,7 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
const mapStateToProps = (state: IApplicationState) => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
customEmojis: state.customEmojis,
|
customEmojis: state.customEmojis,
|
||||||
|
|
|
@ -171,16 +171,11 @@ class ModalBlockView extends React.Component<IModalBlockViewProps, IModalBlockVi
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cancel = async ({ closeModal }: { closeModal?: () => void }) => {
|
cancel = async () => {
|
||||||
const { data } = this.state;
|
const { data } = this.state;
|
||||||
const { appId, viewId, view } = data;
|
const { appId, viewId, view } = data;
|
||||||
|
|
||||||
// handle tablet case
|
|
||||||
if (closeModal) {
|
|
||||||
closeModal();
|
|
||||||
} else {
|
|
||||||
Navigation.back();
|
Navigation.back();
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await triggerCancel({
|
await triggerCancel({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FlatList, StyleSheet, TextInputProps, View } from 'react-native';
|
import { FlatList, StyleSheet, TextInputProps, View } from 'react-native';
|
||||||
|
|
||||||
import TextInput from '../../../containers/TextInput';
|
import FormTextInput from '../../../containers/TextInput/FormTextInput';
|
||||||
import * as List from '../../../containers/List';
|
import * as List from '../../../containers/List';
|
||||||
import { themes } from '../../../lib/constants';
|
import { themes } from '../../../lib/constants';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
|
@ -51,7 +51,7 @@ const ServerInput = ({
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TextInput
|
<FormTextInput
|
||||||
label={I18n.t('Enter_workspace_URL')}
|
label={I18n.t('Enter_workspace_URL')}
|
||||||
placeholder={I18n.t('Workspace_URL_Example')}
|
placeholder={I18n.t('Workspace_URL_Example')}
|
||||||
containerStyle={styles.inputContainer}
|
containerStyle={styles.inputContainer}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue