Merge branch 'develop' into feat.new-audio-player
This commit is contained in:
commit
9854f1626d
|
@ -7,7 +7,7 @@ orbs:
|
||||||
macos: &macos
|
macos: &macos
|
||||||
macos:
|
macos:
|
||||||
xcode: "14.2.0"
|
xcode: "14.2.0"
|
||||||
resource_class: macos.m1.large.gen1
|
resource_class: macos.m1.medium.gen1
|
||||||
|
|
||||||
bash-env: &bash-env
|
bash-env: &bash-env
|
||||||
BASH_ENV: "~/.nvm/nvm.sh"
|
BASH_ENV: "~/.nvm/nvm.sh"
|
||||||
|
@ -453,7 +453,7 @@ jobs:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
executor:
|
executor:
|
||||||
name: android/android-machine
|
name: android/android-machine
|
||||||
resource-class: xlarge
|
resource-class: large
|
||||||
tag: 2022.12.1
|
tag: 2022.12.1
|
||||||
environment:
|
environment:
|
||||||
<<: *android-env
|
<<: *android-env
|
||||||
|
@ -500,7 +500,7 @@ jobs:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
executor:
|
executor:
|
||||||
name: android/android-machine
|
name: android/android-machine
|
||||||
resource-class: xlarge
|
resource-class: large
|
||||||
tag: 2022.12.1
|
tag: 2022.12.1
|
||||||
parallelism: 4
|
parallelism: 4
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -33,8 +33,10 @@ const getStories = () => {
|
||||||
require("../app/containers/ReactionsList/ReactionsList.stories.tsx"),
|
require("../app/containers/ReactionsList/ReactionsList.stories.tsx"),
|
||||||
require("../app/containers/RoomHeader/RoomHeader.stories.tsx"),
|
require("../app/containers/RoomHeader/RoomHeader.stories.tsx"),
|
||||||
require("../app/containers/RoomItem/RoomItem.stories.tsx"),
|
require("../app/containers/RoomItem/RoomItem.stories.tsx"),
|
||||||
|
require("../app/containers/RoomTypeIcon/RoomTypeIcon.stories.tsx"),
|
||||||
require("../app/containers/SearchBox/SearchBox.stories.tsx"),
|
require("../app/containers/SearchBox/SearchBox.stories.tsx"),
|
||||||
require("../app/containers/ServerItem/ServerItem.stories.tsx"),
|
require("../app/containers/ServerItem/ServerItem.stories.tsx"),
|
||||||
|
require("../app/containers/Status/Status.stories.tsx"),
|
||||||
require("../app/containers/TextInput/TextInput.stories.tsx"),
|
require("../app/containers/TextInput/TextInput.stories.tsx"),
|
||||||
require("../app/containers/UIKit/UiKitMessage.stories.tsx"),
|
require("../app/containers/UIKit/UiKitMessage.stories.tsx"),
|
||||||
require("../app/containers/UIKit/UiKitModal.stories.tsx"),
|
require("../app/containers/UIKit/UiKitModal.stories.tsx"),
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,3 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots RoomTypeIcon All 1`] = `"[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#cbced1\\"},[{\\"width\\":30,\\"height\\":30,\\"textAlignVertical\\":\\"center\\"},[{\\"marginRight\\":4},null]],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#2de0a5\\"},[{\\"width\\":30,\\"height\\":30,\\"textAlignVertical\\":\\"center\\"},[{\\"marginRight\\":4},null]],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#ffd21f\\"},[{\\"marginRight\\":4},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":30,\\"color\\":\\"#0d0e12\\"},[{\\"marginRight\\":4},{\\"margin\\":10}],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]"`;
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots Status All 1`] = `"[{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#2de0a5\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#f5455c\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#ffd21f\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#9ea2a8\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#F38C39\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#cbced1\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":32,\\"color\\":\\"#cbced1\\"},[{\\"width\\":32,\\"height\\":32,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]},{\\"type\\":\\"Text\\",\\"props\\":{\\"selectable\\":false,\\"allowFontScaling\\":false,\\"style\\":[{\\"fontSize\\":60,\\"color\\":\\"#2de0a5\\"},[{\\"width\\":60,\\"height\\":60,\\"textAlignVertical\\":\\"center\\"},null],{\\"fontFamily\\":\\"custom\\",\\"fontWeight\\":\\"normal\\",\\"fontStyle\\":\\"normal\\"},{}]},\\"children\\":[\\"\\"]}]"`;
|
|
@ -14,6 +14,8 @@ exports[`Storyshots UIKit/UiKitModal Modal - Images 1`] = `"{\\"type\\":\\"RCTSc
|
||||||
|
|
||||||
exports[`Storyshots UIKit/UiKitModal Modal - Input with error 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
exports[`Storyshots UIKit/UiKitModal Modal - Input with error 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
|
exports[`Storyshots UIKit/UiKitModal Modal - Multi Select Input 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
exports[`Storyshots UIKit/UiKitModal Modal - Multilne with error 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
exports[`Storyshots UIKit/UiKitModal Modal - Multilne with error 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
||||||
|
|
||||||
exports[`Storyshots UIKit/UiKitModal Modal - Section and Accessories 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
exports[`Storyshots UIKit/UiKitModal Modal - Section and Accessories 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"backgroundColor\\":\\"#fff\\"},{\\"paddingHorizontal\\":16}],\\"keyboardShouldPersistTaps\\":\\"always\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":null}]}"`;
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -147,7 +147,7 @@ android {
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "4.40.0"
|
versionName "4.41.0"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
if (!isFoss) {
|
if (!isFoss) {
|
||||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||||
|
|
|
@ -1,22 +1,16 @@
|
||||||
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, useCallback } from 'react';
|
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState, useCallback } from 'react';
|
||||||
import { Keyboard } from 'react-native';
|
import { Keyboard, useWindowDimensions } from 'react-native';
|
||||||
import { Easing } from 'react-native-reanimated';
|
import { Easing, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
||||||
import BottomSheet, { BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
import BottomSheet, { BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
||||||
|
|
||||||
import { useDimensions, useOrientation } from '../../dimensions';
|
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { isIOS, isTablet } from '../../lib/methods/helpers';
|
import { isIOS, isTablet } from '../../lib/methods/helpers';
|
||||||
import { Handle } from './Handle';
|
import { Handle } from './Handle';
|
||||||
import { TActionSheetOptions } from './Provider';
|
import { TActionSheetOptions } from './Provider';
|
||||||
import BottomSheetContent from './BottomSheetContent';
|
import BottomSheetContent from './BottomSheetContent';
|
||||||
import styles, { ITEM_HEIGHT } from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
|
||||||
const MIN_SNAP_HEIGHT = 16;
|
|
||||||
const CANCEL_HEIGHT = 64;
|
|
||||||
|
|
||||||
export const ACTION_SHEET_ANIMATION_DURATION = 250;
|
export const ACTION_SHEET_ANIMATION_DURATION = 250;
|
||||||
|
|
||||||
|
@ -32,33 +26,34 @@ const ActionSheet = React.memo(
|
||||||
const bottomSheetRef = useRef<BottomSheet>(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 { width, height } = useWindowDimensions();
|
||||||
const { isLandscape } = useOrientation();
|
const isLandscape = width > height;
|
||||||
const insets = useSafeAreaInsets();
|
const animatedContentHeight = useSharedValue(0);
|
||||||
|
const animatedHandleHeight = useSharedValue(0);
|
||||||
|
const animatedDataSnaps = useSharedValue<TActionSheetOptions['snaps']>([]);
|
||||||
|
const animatedSnapPoints = useDerivedValue(() => {
|
||||||
|
if (animatedDataSnaps.value?.length) {
|
||||||
|
return animatedDataSnaps.value;
|
||||||
|
}
|
||||||
|
const contentWithHandleHeight = animatedContentHeight.value + animatedHandleHeight.value;
|
||||||
|
// Bottom sheet requires a default value to work
|
||||||
|
if (contentWithHandleHeight === 0) {
|
||||||
|
return ['25%'];
|
||||||
|
}
|
||||||
|
return [contentWithHandleHeight];
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const maxSnap = Math.min(
|
const handleContentLayout = useCallback(
|
||||||
// Items height
|
({
|
||||||
ITEM_HEIGHT * (data?.options?.length || 0) +
|
nativeEvent: {
|
||||||
// Handle height
|
layout: { height }
|
||||||
HANDLE_HEIGHT +
|
}
|
||||||
// Custom header height
|
}) => {
|
||||||
(data?.headerHeight || 0) +
|
animatedContentHeight.value = height;
|
||||||
// Insets bottom height (Notch devices)
|
},
|
||||||
insets.bottom +
|
[animatedContentHeight]
|
||||||
// Cancel button height
|
|
||||||
(data?.hasCancel ? CANCEL_HEIGHT : 0),
|
|
||||||
height - MIN_SNAP_HEIGHT
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
* if the action sheet cover more
|
|
||||||
* than 60% of the whole screen
|
|
||||||
* and it's not at the landscape mode
|
|
||||||
* we'll provide more one snap
|
|
||||||
* that point 50% of the whole screen
|
|
||||||
*/
|
|
||||||
const snaps = maxSnap > height * 0.6 && !isLandscape && !data.snaps ? [height * 0.5, maxSnap] : [maxSnap];
|
|
||||||
|
|
||||||
const toggleVisible = () => setVisible(!isVisible);
|
const toggleVisible = () => setVisible(!isVisible);
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
|
@ -67,6 +62,9 @@ const ActionSheet = React.memo(
|
||||||
|
|
||||||
const show = (options: TActionSheetOptions) => {
|
const show = (options: TActionSheetOptions) => {
|
||||||
setData(options);
|
setData(options);
|
||||||
|
if (options.snaps?.length) {
|
||||||
|
animatedDataSnaps.value = options.snaps;
|
||||||
|
}
|
||||||
toggleVisible();
|
toggleVisible();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -104,6 +102,7 @@ const ActionSheet = React.memo(
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
toggleVisible();
|
toggleVisible();
|
||||||
data?.onClose && data?.onClose();
|
data?.onClose && data?.onClose();
|
||||||
|
animatedDataSnaps.value = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
const renderBackdrop = useCallback(
|
||||||
|
@ -131,7 +130,10 @@ const ActionSheet = React.memo(
|
||||||
{isVisible && (
|
{isVisible && (
|
||||||
<BottomSheet
|
<BottomSheet
|
||||||
ref={bottomSheetRef}
|
ref={bottomSheetRef}
|
||||||
snapPoints={data?.snaps ? data.snaps : snaps}
|
snapPoints={animatedSnapPoints}
|
||||||
|
handleHeight={animatedHandleHeight}
|
||||||
|
// We need undefined to enable vertical swipe gesture inside the bottom sheet like in reaction picker
|
||||||
|
contentHeight={data.snaps?.length ? undefined : animatedContentHeight}
|
||||||
animationConfigs={ANIMATION_CONFIG}
|
animationConfigs={ANIMATION_CONFIG}
|
||||||
animateOnMount={true}
|
animateOnMount={true}
|
||||||
backdropComponent={renderBackdrop}
|
backdropComponent={renderBackdrop}
|
||||||
|
@ -144,7 +146,13 @@ const ActionSheet = React.memo(
|
||||||
enableContentPanningGesture={data?.enableContentPanningGesture ?? true}
|
enableContentPanningGesture={data?.enableContentPanningGesture ?? true}
|
||||||
{...androidTablet}
|
{...androidTablet}
|
||||||
>
|
>
|
||||||
<BottomSheetContent options={data?.options} hide={hide} children={data?.children} hasCancel={data?.hasCancel} />
|
<BottomSheetContent
|
||||||
|
options={data?.options}
|
||||||
|
hide={hide}
|
||||||
|
children={data?.children}
|
||||||
|
hasCancel={data?.hasCancel}
|
||||||
|
onLayout={handleContentLayout}
|
||||||
|
/>
|
||||||
</BottomSheet>
|
</BottomSheet>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Text } from 'react-native';
|
import { Text, ViewProps } from 'react-native';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BottomSheetView, BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
import { BottomSheetView, BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
||||||
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
@ -15,10 +16,12 @@ interface IBottomSheetContentProps {
|
||||||
options?: TActionSheetOptionsItem[];
|
options?: TActionSheetOptionsItem[];
|
||||||
hide: () => void;
|
hide: () => void;
|
||||||
children?: React.ReactElement | null;
|
children?: React.ReactElement | null;
|
||||||
|
onLayout: ViewProps['onLayout'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: IBottomSheetContentProps) => {
|
const BottomSheetContent = React.memo(({ options, hasCancel, hide, children, onLayout }: IBottomSheetContentProps) => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
const { bottom } = useSafeAreaInsets();
|
||||||
|
|
||||||
const renderFooter = () =>
|
const renderFooter = () =>
|
||||||
hasCancel ? (
|
hasCancel ? (
|
||||||
|
@ -42,18 +45,19 @@ const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: I
|
||||||
keyExtractor={item => item.title}
|
keyExtractor={item => item.title}
|
||||||
bounces={true}
|
bounces={true}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
style={{ backgroundColor: colors.focusedBackground }}
|
style={{ backgroundColor: colors.focusedBackground, paddingBottom: bottom }}
|
||||||
keyboardDismissMode='interactive'
|
keyboardDismissMode='interactive'
|
||||||
indicatorStyle='black'
|
indicatorStyle='black'
|
||||||
contentContainerStyle={styles.content}
|
contentContainerStyle={styles.content}
|
||||||
ItemSeparatorComponent={List.Separator}
|
ItemSeparatorComponent={List.Separator}
|
||||||
ListHeaderComponent={List.Separator}
|
ListHeaderComponent={List.Separator}
|
||||||
ListFooterComponent={renderFooter}
|
ListFooterComponent={renderFooter}
|
||||||
|
onLayout={onLayout}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<BottomSheetView testID='action-sheet' style={styles.contentContainer}>
|
<BottomSheetView testID='action-sheet' style={[styles.contentContainer, { paddingBottom: bottom }]} onLayout={onLayout}>
|
||||||
{children}
|
{children}
|
||||||
</BottomSheetView>
|
</BottomSheetView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,6 @@ export type TActionSheetOptionsItem = {
|
||||||
|
|
||||||
export type TActionSheetOptions = {
|
export type TActionSheetOptions = {
|
||||||
options?: TActionSheetOptionsItem[];
|
options?: TActionSheetOptionsItem[];
|
||||||
headerHeight?: number;
|
|
||||||
customHeader?: React.ReactElement | null;
|
customHeader?: React.ReactElement | null;
|
||||||
hasCancel?: boolean;
|
hasCancel?: boolean;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Text, View, useWindowDimensions } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
@ -37,21 +37,16 @@ const IncomingCallHeader = React.memo(
|
||||||
const [mic, setMic] = useState(true);
|
const [mic, setMic] = useState(true);
|
||||||
const [cam, setCam] = useState(false);
|
const [cam, setCam] = useState(false);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
const styles = useStyle();
|
const styles = useStyle();
|
||||||
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { height, width } = useWindowDimensions();
|
|
||||||
const isLandscape = width > height;
|
|
||||||
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
(isMasterDetail || isLandscape) && styles.small,
|
isMasterDetail && styles.small,
|
||||||
{
|
{
|
||||||
marginTop: insets.top
|
marginTop: insets.top
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { ROW_HEIGHT } from '../RoomItem';
|
import { ROW_HEIGHT } from '../RoomItem';
|
||||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||||
import { useOrientation } from '../../dimensions';
|
|
||||||
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
||||||
import { hideNotification } from '../../lib/methods/helpers/notifications';
|
import { hideNotification } from '../../lib/methods/helpers/notifications';
|
||||||
|
|
||||||
|
@ -76,8 +75,6 @@ const styles = StyleSheet.create({
|
||||||
const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => {
|
const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const { isLandscape } = useOrientation();
|
|
||||||
|
|
||||||
const { text, payload } = notification;
|
const { text, payload } = notification;
|
||||||
const { type, rid } = payload;
|
const { type, rid } = payload;
|
||||||
const name = type === 'd' ? payload.sender.username : payload.name;
|
const name = type === 'd' ? payload.sender.username : payload.name;
|
||||||
|
@ -104,7 +101,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.container,
|
styles.container,
|
||||||
(isMasterDetail || isLandscape) && styles.small,
|
isMasterDetail && styles.small,
|
||||||
{
|
{
|
||||||
backgroundColor: themes[theme].focusedBackground,
|
backgroundColor: themes[theme].focusedBackground,
|
||||||
borderColor: themes[theme].separatorColor,
|
borderColor: themes[theme].separatorColor,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
import { FlatList, StyleSheet, Text, View, useWindowDimensions } from 'react-native';
|
||||||
|
|
||||||
import { TSupportedThemes, useTheme } from '../../theme';
|
import { TSupportedThemes, useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
|
@ -8,7 +8,6 @@ import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
||||||
import { addFrequentlyUsed } from '../../lib/methods';
|
import { addFrequentlyUsed } from '../../lib/methods';
|
||||||
import { useFrequentlyUsedEmoji } from '../../lib/hooks';
|
import { useFrequentlyUsedEmoji } from '../../lib/hooks';
|
||||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||||
import { useDimensions } from '../../dimensions';
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { IEmoji, TAnyMessageModel } from '../../definitions';
|
import { IEmoji, TAnyMessageModel } from '../../definitions';
|
||||||
import Touch from '../Touch';
|
import Touch from '../Touch';
|
||||||
|
@ -32,7 +31,6 @@ interface THeaderFooter {
|
||||||
theme: TSupportedThemes;
|
theme: TSupportedThemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HEADER_HEIGHT = 36;
|
|
||||||
const ITEM_SIZE = 36;
|
const ITEM_SIZE = 36;
|
||||||
const CONTAINER_MARGIN = 8;
|
const CONTAINER_MARGIN = 8;
|
||||||
const ITEM_MARGIN = 8;
|
const ITEM_MARGIN = 8;
|
||||||
|
@ -86,11 +84,10 @@ const HeaderFooter = ({ onReaction, theme }: THeaderFooter) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader) => {
|
const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader) => {
|
||||||
const { width, height } = useDimensions();
|
const { width } = useWindowDimensions();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji(true);
|
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji(true);
|
||||||
const isLandscape = width > height;
|
const size = (isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||||
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
|
||||||
const quantity = Math.trunc(size / (ITEM_SIZE + ITEM_MARGIN * 2) - 1);
|
const quantity = Math.trunc(size / (ITEM_SIZE + ITEM_MARGIN * 2) - 1);
|
||||||
|
|
||||||
const onReaction: TOnReaction = ({ emoji }) => {
|
const onReaction: TOnReaction = ({ emoji }) => {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
||||||
import { TActionSheetOptionsItem, useActionSheet, ACTION_SHEET_ANIMATION_DURATION } from '../ActionSheet';
|
import { TActionSheetOptionsItem, useActionSheet, ACTION_SHEET_ANIMATION_DURATION } from '../ActionSheet';
|
||||||
import Header, { HEADER_HEIGHT, IHeader } from './Header';
|
import Header, { IHeader } from './Header';
|
||||||
import events from '../../lib/methods/helpers/log/events';
|
import events from '../../lib/methods/helpers/log/events';
|
||||||
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||||
import { getPermalinkMessage } from '../../lib/methods';
|
import { getPermalinkMessage } from '../../lib/methods';
|
||||||
|
@ -511,7 +511,6 @@ const MessageActions = React.memo(
|
||||||
await getPermissions();
|
await getPermissions();
|
||||||
showActionSheet({
|
showActionSheet({
|
||||||
options: getOptions(message),
|
options: getOptions(message),
|
||||||
headerHeight: HEADER_HEIGHT,
|
|
||||||
customHeader:
|
customHeader:
|
||||||
!isReadOnly || room.reactWhenReadOnly ? (
|
!isReadOnly || room.reactWhenReadOnly ? (
|
||||||
<Header handleReaction={handleReaction} isMasterDetail={isMasterDetail} message={message} />
|
<Header handleReaction={handleReaction} isMasterDetail={isMasterDetail} message={message} />
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import React, { forwardRef, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react';
|
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||||
import { Col, Grid, Row } from 'react-native-easy-grid';
|
import { Col, Grid, Row } from 'react-native-easy-grid';
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import * as Animatable from 'react-native-animatable';
|
import * as Animatable from 'react-native-animatable';
|
||||||
import * as Haptics from 'expo-haptics';
|
import * as Haptics from 'expo-haptics';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
|
@ -16,7 +15,6 @@ import LockIcon from './LockIcon';
|
||||||
import Title from './Title';
|
import Title from './Title';
|
||||||
import Subtitle from './Subtitle';
|
import Subtitle from './Subtitle';
|
||||||
import { useDimensions } from '../../../dimensions';
|
import { useDimensions } from '../../../dimensions';
|
||||||
import { isTablet } from '../../../lib/methods/helpers';
|
|
||||||
|
|
||||||
interface IPasscodeBase {
|
interface IPasscodeBase {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -37,18 +35,6 @@ export interface IBase {
|
||||||
|
|
||||||
const Base = forwardRef<IBase, IPasscodeBase>(
|
const Base = forwardRef<IBase, IPasscodeBase>(
|
||||||
({ type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress }, ref) => {
|
({ type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress }, ref) => {
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.lockToPortrait();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.unlockAllOrientations();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { height } = useDimensions();
|
const { height } = useDimensions();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { IReaction } from '../../definitions';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import styles, { MIN_TAB_WIDTH } from './styles';
|
import styles, { MIN_TAB_WIDTH } from './styles';
|
||||||
import { useDimensions, useOrientation } from '../../dimensions';
|
import { useDimensions } from '../../dimensions';
|
||||||
|
|
||||||
interface ITabBarItem {
|
interface ITabBarItem {
|
||||||
getCustomEmoji: TGetCustomEmoji;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
|
@ -55,10 +55,8 @@ const TabBarItem = ({ tab, index, goToPage, getCustomEmoji }: ITabBarItem) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ReactionsTabBar = ({ tabs, activeTab, goToPage, getCustomEmoji }: IReactionsTabBar): React.ReactElement => {
|
const ReactionsTabBar = ({ tabs, activeTab, goToPage, getCustomEmoji }: IReactionsTabBar): React.ReactElement => {
|
||||||
const { isLandscape } = useOrientation();
|
|
||||||
const { width } = useDimensions();
|
const { width } = useDimensions();
|
||||||
const reactionsListWidth = isLandscape ? width / 2 : width;
|
const tabWidth = tabs && Math.max(width / tabs.length, MIN_TAB_WIDTH);
|
||||||
const tabWidth = tabs && Math.max(reactionsListWidth / tabs.length, MIN_TAB_WIDTH);
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View testID='reactionsTabBar'>
|
<View testID='reactionsTabBar'>
|
||||||
|
|
|
@ -66,14 +66,6 @@ export const Typing = () => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Landscape = () => (
|
|
||||||
<>
|
|
||||||
<HeaderExample title={() => <RoomHeader width={height} height={width} />} />
|
|
||||||
<HeaderExample title={() => <RoomHeader width={height} height={width} subtitle='subtitle' />} />
|
|
||||||
<HeaderExample title={() => <RoomHeader width={height} height={width} title={longText} subtitle={longText} />} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Thread = () => (
|
export const Thread = () => (
|
||||||
<>
|
<>
|
||||||
<HeaderExample title={() => <RoomHeader tmid='123' parentTitle='parent title' />} />
|
<HeaderExample title={() => <RoomHeader tmid='123' parentTitle='parent title' />} />
|
||||||
|
|
|
@ -63,10 +63,11 @@ interface IRoomHeader {
|
||||||
type: string;
|
type: string;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
roomUserId?: string | null;
|
||||||
prid?: string;
|
prid?: string;
|
||||||
tmid?: string;
|
tmid?: string;
|
||||||
teamMain?: boolean;
|
teamMain?: boolean;
|
||||||
status: TUserStatus;
|
status?: TUserStatus;
|
||||||
usersTyping: [];
|
usersTyping: [];
|
||||||
isGroupChat?: boolean;
|
isGroupChat?: boolean;
|
||||||
parentTitle?: string;
|
parentTitle?: string;
|
||||||
|
@ -130,6 +131,7 @@ const Header = React.memo(
|
||||||
status,
|
status,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
roomUserId,
|
||||||
prid,
|
prid,
|
||||||
tmid,
|
tmid,
|
||||||
onPress,
|
onPress,
|
||||||
|
@ -154,7 +156,13 @@ const Header = React.memo(
|
||||||
if (tmid) {
|
if (tmid) {
|
||||||
renderFunc = () => (
|
renderFunc = () => (
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
<RoomTypeIcon
|
||||||
|
userId={roomUserId}
|
||||||
|
type={prid ? 'discussion' : type}
|
||||||
|
isGroupChat={isGroupChat}
|
||||||
|
status={status}
|
||||||
|
teamMain={teamMain}
|
||||||
|
/>
|
||||||
<Text style={[styles.subtitle, { color: colors.auxiliaryText }]} numberOfLines={1}>
|
<Text style={[styles.subtitle, { color: colors.auxiliaryText }]} numberOfLines={1}>
|
||||||
{parentTitle}
|
{parentTitle}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -176,6 +184,7 @@ const Header = React.memo(
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
{tmid ? null : (
|
{tmid ? null : (
|
||||||
<RoomTypeIcon
|
<RoomTypeIcon
|
||||||
|
userId={roomUserId}
|
||||||
type={prid ? 'discussion' : type}
|
type={prid ? 'discussion' : type}
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -39,14 +39,13 @@ const RoomHeaderContainer = React.memo(
|
||||||
visitor
|
visitor
|
||||||
}: IRoomHeaderContainerProps) => {
|
}: IRoomHeaderContainerProps) => {
|
||||||
let subtitle: string | undefined;
|
let subtitle: string | undefined;
|
||||||
let status: TUserStatus = 'offline';
|
let statusVisitor: TUserStatus | undefined;
|
||||||
let statusText: string | undefined;
|
let statusText: string | undefined;
|
||||||
const { width, height } = useDimensions();
|
const { width, height } = useDimensions();
|
||||||
|
|
||||||
const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
|
const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
|
||||||
const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
|
const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
|
||||||
const connected = useSelector((state: IApplicationState) => state.meteor.connected);
|
const connected = useSelector((state: IApplicationState) => state.meteor.connected);
|
||||||
const presenceDisabled = useSelector((state: IApplicationState) => state.settings.Presence_broadcast_disabled);
|
|
||||||
const activeUser = useSelector(
|
const activeUser = useSelector(
|
||||||
(state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
|
(state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
|
||||||
shallowEqual
|
shallowEqual
|
||||||
|
@ -62,28 +61,23 @@ const RoomHeaderContainer = React.memo(
|
||||||
|
|
||||||
if (connected) {
|
if (connected) {
|
||||||
if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
|
if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
|
||||||
if (presenceDisabled) {
|
const { statusText: statusTextActiveUser } = activeUser;
|
||||||
status = 'disabled';
|
statusText = statusTextActiveUser;
|
||||||
} else {
|
|
||||||
const { status: statusActiveUser, statusText: statusTextActiveUser } = activeUser;
|
|
||||||
status = statusActiveUser;
|
|
||||||
statusText = statusTextActiveUser;
|
|
||||||
}
|
|
||||||
} else if (type === 'l' && visitor?.status) {
|
} else if (type === 'l' && visitor?.status) {
|
||||||
const { status: statusVisitor } = visitor;
|
({ status: statusVisitor } = visitor);
|
||||||
status = statusVisitor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoomHeader
|
<RoomHeader
|
||||||
|
roomUserId={roomUserId}
|
||||||
prid={prid}
|
prid={prid}
|
||||||
tmid={tmid}
|
tmid={tmid}
|
||||||
title={title}
|
title={title}
|
||||||
subtitle={type === 'd' ? statusText : subtitle}
|
subtitle={type === 'd' ? statusText : subtitle}
|
||||||
type={type}
|
type={type}
|
||||||
teamMain={teamMain}
|
teamMain={teamMain}
|
||||||
status={status}
|
status={statusVisitor}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
usersTyping={usersTyping}
|
usersTyping={usersTyping}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const IconOrAvatar = ({
|
||||||
type,
|
type,
|
||||||
rid,
|
rid,
|
||||||
showAvatar,
|
showAvatar,
|
||||||
|
userId,
|
||||||
prid,
|
prid,
|
||||||
status,
|
status,
|
||||||
isGroupChat,
|
isGroupChat,
|
||||||
|
@ -30,6 +31,7 @@ const IconOrAvatar = ({
|
||||||
return (
|
return (
|
||||||
<View style={styles.typeIcon}>
|
<View style={styles.typeIcon}>
|
||||||
<TypeIcon
|
<TypeIcon
|
||||||
|
userId={userId}
|
||||||
type={type}
|
type={type}
|
||||||
prid={prid}
|
prid={prid}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -41,14 +41,14 @@ export const Touch = () => <RoomItem onPress={() => alert('on press')} onLongPre
|
||||||
|
|
||||||
export const User = () => (
|
export const User = () => (
|
||||||
<>
|
<>
|
||||||
<RoomItem name='diego.mello' avatar='diego.mello' />
|
<RoomItem name='diego.mello' avatar='diego.mello' userId='abc' />
|
||||||
<RoomItem name={longText} />
|
<RoomItem name={longText} userId='abc' />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Type = () => (
|
export const Type = () => (
|
||||||
<>
|
<>
|
||||||
<RoomItem type='d' />
|
<RoomItem type='d' userId='abc' />
|
||||||
<RoomItem type='c' />
|
<RoomItem type='c' />
|
||||||
<RoomItem type='p' />
|
<RoomItem type='p' />
|
||||||
<RoomItem type='l' />
|
<RoomItem type='l' />
|
||||||
|
@ -58,18 +58,6 @@ export const Type = () => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const UserStatus = () => (
|
|
||||||
<>
|
|
||||||
<RoomItem status='online' />
|
|
||||||
<RoomItem status='away' />
|
|
||||||
<RoomItem status='busy' />
|
|
||||||
<RoomItem status='offline' />
|
|
||||||
<RoomItem status='loading' />
|
|
||||||
<RoomItem status='disabled' />
|
|
||||||
<RoomItem status='wrong' />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Alerts = () => (
|
export const Alerts = () => (
|
||||||
<>
|
<>
|
||||||
<RoomItem alert />
|
<RoomItem alert />
|
||||||
|
@ -159,7 +147,6 @@ export const ExpandedRoomItemWithoutAvatar = () => (
|
||||||
showAvatar={false}
|
showAvatar={false}
|
||||||
/>
|
/>
|
||||||
<RoomItem
|
<RoomItem
|
||||||
status='online'
|
|
||||||
showLastMessage
|
showLastMessage
|
||||||
alert
|
alert
|
||||||
tunread={[1]}
|
tunread={[1]}
|
||||||
|
@ -167,14 +154,7 @@ export const ExpandedRoomItemWithoutAvatar = () => (
|
||||||
displayMode={DisplayMode.Expanded}
|
displayMode={DisplayMode.Expanded}
|
||||||
showAvatar={false}
|
showAvatar={false}
|
||||||
/>
|
/>
|
||||||
<RoomItem
|
<RoomItem showLastMessage alert lastMessage={lastMessage} displayMode={DisplayMode.Expanded} showAvatar={false} />
|
||||||
status='online'
|
|
||||||
showLastMessage
|
|
||||||
alert
|
|
||||||
lastMessage={lastMessage}
|
|
||||||
displayMode={DisplayMode.Expanded}
|
|
||||||
showAvatar={false}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { IRoomItemProps } from './interfaces';
|
||||||
|
|
||||||
const RoomItem = ({
|
const RoomItem = ({
|
||||||
rid,
|
rid,
|
||||||
|
userId,
|
||||||
type,
|
type,
|
||||||
prid,
|
prid,
|
||||||
name,
|
name,
|
||||||
|
@ -74,6 +75,7 @@ const RoomItem = ({
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
avatar={avatar}
|
avatar={avatar}
|
||||||
type={type}
|
type={type}
|
||||||
|
userId={userId}
|
||||||
rid={rid}
|
rid={rid}
|
||||||
prid={prid}
|
prid={prid}
|
||||||
status={status}
|
status={status}
|
||||||
|
@ -89,6 +91,7 @@ const RoomItem = ({
|
||||||
<View style={styles.titleContainer}>
|
<View style={styles.titleContainer}>
|
||||||
{showAvatar ? (
|
{showAvatar ? (
|
||||||
<TypeIcon
|
<TypeIcon
|
||||||
|
userId={userId}
|
||||||
type={type}
|
type={type}
|
||||||
prid={prid}
|
prid={prid}
|
||||||
status={status}
|
status={status}
|
||||||
|
@ -125,6 +128,7 @@ const RoomItem = ({
|
||||||
) : (
|
) : (
|
||||||
<View style={[styles.titleContainer, styles.flex]}>
|
<View style={[styles.titleContainer, styles.flex]}>
|
||||||
<TypeIcon
|
<TypeIcon
|
||||||
|
userId={userId}
|
||||||
type={type}
|
type={type}
|
||||||
prid={prid}
|
prid={prid}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -3,8 +3,9 @@ import React from 'react';
|
||||||
import RoomTypeIcon from '../RoomTypeIcon';
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
import { ITypeIconProps } from './interfaces';
|
import { ITypeIconProps } from './interfaces';
|
||||||
|
|
||||||
const TypeIcon = React.memo(({ type, prid, status, isGroupChat, teamMain, size, style, sourceType }: ITypeIconProps) => (
|
const TypeIcon = React.memo(({ userId, type, prid, status, isGroupChat, teamMain, size, style, sourceType }: ITypeIconProps) => (
|
||||||
<RoomTypeIcon
|
<RoomTypeIcon
|
||||||
|
userId={userId}
|
||||||
type={prid ? 'discussion' : type}
|
type={prid ? 'discussion' : type}
|
||||||
isGroupChat={isGroupChat}
|
isGroupChat={isGroupChat}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -2,13 +2,11 @@ import React, { useEffect, useReducer, useRef } from 'react';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { getUserPresence } from '../../lib/methods';
|
|
||||||
import { isGroupChat } from '../../lib/methods/helpers';
|
import { isGroupChat } from '../../lib/methods/helpers';
|
||||||
import { formatDate } from '../../lib/methods/helpers/room';
|
import { formatDate } from '../../lib/methods/helpers/room';
|
||||||
import { IRoomItemContainerProps } from './interfaces';
|
import { IRoomItemContainerProps } from './interfaces';
|
||||||
import RoomItem from './RoomItem';
|
import RoomItem from './RoomItem';
|
||||||
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
||||||
import { useUserStatus } from './useUserStatus';
|
|
||||||
|
|
||||||
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
||||||
|
|
||||||
|
@ -44,8 +42,7 @@ const RoomItemContainer = React.memo(
|
||||||
const alert = item.alert || item.tunread?.length;
|
const alert = item.alert || item.tunread?.length;
|
||||||
const [_, forceUpdate] = useReducer(x => x + 1, 1);
|
const [_, forceUpdate] = useReducer(x => x + 1, 1);
|
||||||
const roomSubscription = useRef<Subscription | null>(null);
|
const roomSubscription = useRef<Subscription | null>(null);
|
||||||
|
const userId = item.t === 'd' && id && !isGroupChat(item) ? id : null;
|
||||||
const { connected, status } = useUserStatus(item.t, item?.visitor?.status, id);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const init = () => {
|
const init = () => {
|
||||||
|
@ -61,13 +58,6 @@ const RoomItemContainer = React.memo(
|
||||||
return () => roomSubscription.current?.unsubscribe();
|
return () => roomSubscription.current?.unsubscribe();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const isDirect = !!(item.t === 'd' && id && !isGroupChat(item));
|
|
||||||
if (connected && isDirect) {
|
|
||||||
getUserPresence(id);
|
|
||||||
}
|
|
||||||
}, [connected]);
|
|
||||||
|
|
||||||
const handleOnPress = () => onPress(item);
|
const handleOnPress = () => onPress(item);
|
||||||
|
|
||||||
const handleOnLongPress = () => onLongPress && onLongPress(item);
|
const handleOnLongPress = () => onLongPress && onLongPress(item);
|
||||||
|
@ -98,6 +88,7 @@ const RoomItemContainer = React.memo(
|
||||||
width={width}
|
width={width}
|
||||||
favorite={item.f}
|
favorite={item.f}
|
||||||
rid={item.rid}
|
rid={item.rid}
|
||||||
|
userId={userId}
|
||||||
toggleFav={toggleFav}
|
toggleFav={toggleFav}
|
||||||
toggleRead={toggleRead}
|
toggleRead={toggleRead}
|
||||||
hideChannel={hideChannel}
|
hideChannel={hideChannel}
|
||||||
|
@ -105,7 +96,6 @@ const RoomItemContainer = React.memo(
|
||||||
type={item.t}
|
type={item.t}
|
||||||
isFocused={isFocused}
|
isFocused={isFocused}
|
||||||
prid={item.prid}
|
prid={item.prid}
|
||||||
status={status}
|
|
||||||
hideUnreadStatus={item.hideUnreadStatus}
|
hideUnreadStatus={item.hideUnreadStatus}
|
||||||
hideMentionStatus={item.hideMentionStatus}
|
hideMentionStatus={item.hideMentionStatus}
|
||||||
alert={alert}
|
alert={alert}
|
||||||
|
@ -124,7 +114,8 @@ const RoomItemContainer = React.memo(
|
||||||
autoJoin={autoJoin}
|
autoJoin={autoJoin}
|
||||||
showAvatar={showAvatar}
|
showAvatar={showAvatar}
|
||||||
displayMode={displayMode}
|
displayMode={displayMode}
|
||||||
sourceType={item.source}
|
status={item.t === 'l' ? item?.visitor?.status : null}
|
||||||
|
sourceType={item.t === 'l' ? item.source : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -37,6 +37,7 @@ export interface IWrapperProps {
|
||||||
accessibilityLabel: string;
|
accessibilityLabel: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
userId: string | null;
|
||||||
rid: string;
|
rid: string;
|
||||||
children: React.ReactElement;
|
children: React.ReactElement;
|
||||||
displayMode: string;
|
displayMode: string;
|
||||||
|
@ -50,6 +51,7 @@ export interface IWrapperProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITypeIconProps {
|
export interface ITypeIconProps {
|
||||||
|
userId: string | null;
|
||||||
type: string;
|
type: string;
|
||||||
status: TUserStatus;
|
status: TUserStatus;
|
||||||
prid: string;
|
prid: string;
|
||||||
|
@ -144,6 +146,7 @@ export interface IIconOrAvatar {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
type: string;
|
type: string;
|
||||||
rid: string;
|
rid: string;
|
||||||
|
userId: string | null;
|
||||||
showAvatar: boolean;
|
showAvatar: boolean;
|
||||||
displayMode: string;
|
displayMode: string;
|
||||||
prid: string;
|
prid: string;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { TUserStatus } from '../../definitions';
|
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
|
||||||
import { RoomTypes } from '../../lib/methods';
|
|
||||||
|
|
||||||
export const useUserStatus = (
|
|
||||||
type: RoomTypes,
|
|
||||||
liveChatStatus?: TUserStatus,
|
|
||||||
id?: string
|
|
||||||
): { connected: boolean; status: TUserStatus } => {
|
|
||||||
const connected = useAppSelector(state => state.meteor.connected);
|
|
||||||
const presenceDisabled = useAppSelector(state => state.settings.Presence_broadcast_disabled);
|
|
||||||
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
|
|
||||||
|
|
||||||
let status = 'loading';
|
|
||||||
if (connected) {
|
|
||||||
if (type === 'd') {
|
|
||||||
if (presenceDisabled) {
|
|
||||||
status = 'disabled';
|
|
||||||
} else {
|
|
||||||
status = userStatus || 'loading';
|
|
||||||
}
|
|
||||||
} else if (type === 'l' && liveChatStatus) {
|
|
||||||
status = liveChatStatus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
connected,
|
|
||||||
status: status as TUserStatus
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { OmnichannelSourceType } from '../../definitions';
|
||||||
|
import RoomTypeIcon from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'RoomTypeIcon'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const All = () => (
|
||||||
|
<>
|
||||||
|
<RoomTypeIcon size={30} type='d' userId='asd' />
|
||||||
|
<RoomTypeIcon size={30} type='d' userId='asd' status='online' />
|
||||||
|
<RoomTypeIcon size={30} type='d' isGroupChat />
|
||||||
|
<RoomTypeIcon size={30} type='c' />
|
||||||
|
<RoomTypeIcon size={30} type='p' />
|
||||||
|
<RoomTypeIcon size={30} type='c' teamMain />
|
||||||
|
<RoomTypeIcon size={30} type='p' teamMain />
|
||||||
|
<RoomTypeIcon size={30} type='discussion' />
|
||||||
|
<RoomTypeIcon size={30} type='l' status='away' sourceType={{ type: OmnichannelSourceType.SMS }} />
|
||||||
|
<RoomTypeIcon size={30} type='p' style={{ margin: 10 }} />
|
||||||
|
</>
|
||||||
|
);
|
|
@ -3,8 +3,8 @@ import { StyleSheet, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { OmnichannelRoomIcon } from './OmnichannelRoomIcon';
|
import { OmnichannelRoomIcon } from './OmnichannelRoomIcon';
|
||||||
import { CustomIcon, TIconsName } from '../CustomIcon';
|
import { CustomIcon, TIconsName } from '../CustomIcon';
|
||||||
import { STATUS_COLORS, themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import Status from '../Status/Status';
|
import Status from '../Status';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { TUserStatus, IOmnichannelSource } from '../../definitions';
|
import { TUserStatus, IOmnichannelSource } from '../../definitions';
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IRoomTypeIcon {
|
interface IRoomTypeIcon {
|
||||||
|
userId?: string | null;
|
||||||
type: string;
|
type: string;
|
||||||
isGroupChat?: boolean;
|
isGroupChat?: boolean;
|
||||||
teamMain?: boolean;
|
teamMain?: boolean;
|
||||||
|
@ -24,44 +25,38 @@ interface IRoomTypeIcon {
|
||||||
sourceType?: IOmnichannelSource;
|
sourceType?: IOmnichannelSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, teamMain, size = 16, sourceType }: IRoomTypeIcon) => {
|
const RoomTypeIcon = React.memo(
|
||||||
const { theme } = useTheme();
|
({ userId, type, isGroupChat, status, style, teamMain, size = 16, sourceType }: IRoomTypeIcon) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
const color = themes[theme].titleText;
|
|
||||||
const iconStyle = [styles.icon, { color }, style];
|
|
||||||
|
|
||||||
if (type === 'd' && !isGroupChat) {
|
|
||||||
if (!status) {
|
|
||||||
status = 'offline';
|
|
||||||
}
|
}
|
||||||
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'l') {
|
const iconStyle = [styles.icon, style];
|
||||||
return <OmnichannelRoomIcon style={[styles.icon, style]} size={size} type={type} status={status} sourceType={sourceType} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move this to a separate function
|
if (type === 'd' && !isGroupChat && userId) {
|
||||||
let icon: TIconsName = 'channel-private';
|
return <Status id={userId} style={iconStyle} size={size} status={status} />;
|
||||||
if (teamMain) {
|
}
|
||||||
icon = `teams${type === 'p' ? '-private' : ''}`;
|
|
||||||
} else if (type === 'discussion') {
|
if (type === 'l') {
|
||||||
icon = 'discussions';
|
return <OmnichannelRoomIcon style={iconStyle} size={size} type={type} status={status} sourceType={sourceType} />;
|
||||||
} else if (type === 'c') {
|
}
|
||||||
icon = 'channel-public';
|
|
||||||
} else if (type === 'd') {
|
// TODO: move this to a separate function
|
||||||
if (isGroupChat) {
|
let icon: TIconsName = 'channel-private';
|
||||||
|
if (teamMain) {
|
||||||
|
icon = `teams${type === 'p' ? '-private' : ''}`;
|
||||||
|
} else if (type === 'discussion') {
|
||||||
|
icon = 'discussions';
|
||||||
|
} else if (type === 'c') {
|
||||||
|
icon = 'channel-public';
|
||||||
|
} else if (type === 'd' && isGroupChat) {
|
||||||
icon = 'message';
|
icon = 'message';
|
||||||
} else {
|
|
||||||
icon = 'mention';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return <CustomIcon name={icon} size={size} color={color} style={iconStyle} />;
|
return <CustomIcon name={icon} size={size} color={themes[theme].titleText} style={iconStyle} />;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default RoomTypeIcon;
|
export default RoomTypeIcon;
|
||||||
|
|
|
@ -6,8 +6,6 @@ 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 './TextInput';
|
import { TextInput } from './TextInput';
|
||||||
import { isIOS, isTablet } from '../lib/methods/helpers';
|
|
||||||
import { useOrientation } from '../dimensions';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -16,7 +14,8 @@ const styles = StyleSheet.create({
|
||||||
marginLeft: 0
|
marginLeft: 0
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
...sharedStyles.textSemibold
|
...sharedStyles.textSemibold,
|
||||||
|
fontSize: 16
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,15 +27,12 @@ interface ISearchHeaderProps {
|
||||||
const SearchHeader = ({ onSearchChangeText, testID }: ISearchHeaderProps): JSX.Element => {
|
const SearchHeader = ({ onSearchChangeText, testID }: ISearchHeaderProps): JSX.Element => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const isLight = theme === 'light';
|
const isLight = theme === 'light';
|
||||||
const { isLandscape } = useOrientation();
|
|
||||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
|
||||||
const titleFontSize = 16 * scale;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
style={[styles.title, isLight && { color: themes[theme].headerTitleColor }, { fontSize: titleFontSize }]}
|
style={[styles.title, isLight && { color: themes[theme].headerTitleColor }]}
|
||||||
placeholder={I18n.t('Search')}
|
placeholder={I18n.t('Search')}
|
||||||
onChangeText={onSearchChangeText}
|
onChangeText={onSearchChangeText}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Status from './Status';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Status'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const All = () => (
|
||||||
|
<>
|
||||||
|
<Status status='online' />
|
||||||
|
<Status status='busy' />
|
||||||
|
<Status status='away' />
|
||||||
|
<Status status='loading' />
|
||||||
|
<Status status='disabled' />
|
||||||
|
<Status status='offline' />
|
||||||
|
<Status />
|
||||||
|
<Status status='online' size={60} />
|
||||||
|
</>
|
||||||
|
);
|
|
@ -3,9 +3,9 @@ import { StyleProp, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { CustomIcon, IconSet, TIconsName } from '../CustomIcon';
|
import { CustomIcon, IconSet, TIconsName } from '../CustomIcon';
|
||||||
import { STATUS_COLORS } from '../../lib/constants';
|
import { STATUS_COLORS } from '../../lib/constants';
|
||||||
import { IStatus } from './definition';
|
import { IStatusComponentProps } from './definition';
|
||||||
|
|
||||||
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: Omit<IStatus, 'id'>) => {
|
const Status = React.memo(({ style, status = 'offline', size = 32, ...props }: IStatusComponentProps) => {
|
||||||
const name: TIconsName = `status-${status}`;
|
const name: TIconsName = `status-${status}`;
|
||||||
const isNameValid = IconSet.hasIcon(name);
|
const isNameValid = IconSet.hasIcon(name);
|
||||||
const iconName = isNameValid ? name : 'status-offline';
|
const iconName = isNameValid ? name : 'status-offline';
|
||||||
|
|
|
@ -5,5 +5,10 @@ import { TUserStatus } from '../../definitions';
|
||||||
export interface IStatus extends TextProps {
|
export interface IStatus extends TextProps {
|
||||||
id: string;
|
id: string;
|
||||||
size: number;
|
size: number;
|
||||||
status: TUserStatus;
|
status?: TUserStatus | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IStatusComponentProps extends Omit<IStatus, 'id' | 'size' | 'status'> {
|
||||||
|
size?: number;
|
||||||
|
status?: TUserStatus;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
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';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
import { getUserPresence } from '../../lib/methods';
|
||||||
|
|
||||||
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
const StatusContainer = ({ id, style, status, size = 32, ...props }: IStatus): React.ReactElement => {
|
||||||
const status = useAppSelector(state => {
|
const connected = useAppSelector(state => state.meteor.connected);
|
||||||
|
const statusState = useAppSelector(state => {
|
||||||
if (state.settings.Presence_broadcast_disabled) {
|
if (state.settings.Presence_broadcast_disabled) {
|
||||||
return 'disabled';
|
return 'disabled';
|
||||||
}
|
}
|
||||||
if (state.meteor.connected) {
|
if (state.meteor.connected && state.activeUsers[id]) {
|
||||||
return state.activeUsers[id] && state.activeUsers[id].status;
|
return state.activeUsers[id].status;
|
||||||
|
}
|
||||||
|
if (!state.meteor.connected) {
|
||||||
|
return 'offline';
|
||||||
}
|
}
|
||||||
return 'loading';
|
return 'loading';
|
||||||
}) as TUserStatus;
|
});
|
||||||
return <Status size={size} style={style} status={status} {...props} />;
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (connected && statusState === 'loading') {
|
||||||
|
getUserPresence(id);
|
||||||
|
}
|
||||||
|
}, [connected, statusState]);
|
||||||
|
|
||||||
|
return <Status size={size} style={style} status={status ?? statusState} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StatusContainer;
|
export default StatusContainer;
|
||||||
|
|
|
@ -69,7 +69,11 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IT
|
||||||
<View style={styles.detailsContainer}>
|
<View style={styles.detailsContainer}>
|
||||||
<View style={styles.detailContainer}>
|
<View style={styles.detailContainer}>
|
||||||
<CustomIcon name='threads' size={24} color={themes[theme].auxiliaryText} />
|
<CustomIcon name='threads' size={24} color={themes[theme].auxiliaryText} />
|
||||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
<Text
|
||||||
|
testID={`thread-count-${count}`}
|
||||||
|
style={[styles.detailText, { color: themes[theme].auxiliaryText }]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
{count}
|
{count}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -87,8 +87,7 @@ export const MultiSelect = React.memo(
|
||||||
selectedItems={selected}
|
selectedItems={selected}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
onClose,
|
onClose
|
||||||
headerHeight: 275
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onHide = () => {
|
const onHide = () => {
|
||||||
|
|
|
@ -252,6 +252,47 @@ export const SectionMultiSelect = () =>
|
||||||
emoji: true
|
emoji: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'section',
|
||||||
|
text: {
|
||||||
|
type: 'mrkdwn',
|
||||||
|
text: 'Section + select with value undefined'
|
||||||
|
},
|
||||||
|
accessory: {
|
||||||
|
type: 'multi_static_select',
|
||||||
|
appId: 'app-id',
|
||||||
|
blockId: 'block-id',
|
||||||
|
actionId: 'action-id',
|
||||||
|
initialValue: undefined,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
value: 'option_1',
|
||||||
|
text: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'lorem ipsum 🚀',
|
||||||
|
emoji: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'option_2',
|
||||||
|
text: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'lorem ipsum 🚀',
|
||||||
|
emoji: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
placeholder: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Select an item'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Label',
|
||||||
|
emoji: true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
SectionMultiSelect.storyName = 'Section + Multi Select';
|
SectionMultiSelect.storyName = 'Section + Multi Select';
|
||||||
|
|
|
@ -174,7 +174,12 @@ export const ModalFormInput = () =>
|
||||||
text: 'Set a date',
|
text: 'Set a date',
|
||||||
emoji: true
|
emoji: true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
]);
|
||||||
|
ModalFormInput.storyName = 'Modal - Form Input';
|
||||||
|
|
||||||
|
export const ModalMultiSelect = () =>
|
||||||
|
UiKitModal([
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
element: {
|
element: {
|
||||||
|
@ -200,10 +205,48 @@ export const ModalFormInput = () =>
|
||||||
type: 'plain_text',
|
type: 'plain_text',
|
||||||
text: 'Share with...',
|
text: 'Share with...',
|
||||||
emoji: true
|
emoji: true
|
||||||
|
},
|
||||||
|
hint: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Initial Value Undefined',
|
||||||
|
emoji: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
element: {
|
||||||
|
type: 'multi_static_select',
|
||||||
|
initialValue: [1],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'John'
|
||||||
|
},
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Dog'
|
||||||
|
},
|
||||||
|
value: 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Share with...',
|
||||||
|
emoji: true
|
||||||
|
},
|
||||||
|
hint: {
|
||||||
|
type: 'plain_text',
|
||||||
|
text: 'Initial Value as John',
|
||||||
|
emoji: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
ModalFormInput.storyName = 'Modal - Form Input';
|
ModalMultiSelect.storyName = 'Modal - Multi Select Input';
|
||||||
|
|
||||||
export const ModalFormTextArea = () =>
|
export const ModalFormTextArea = () =>
|
||||||
UiKitModal([
|
UiKitModal([
|
||||||
|
|
|
@ -138,7 +138,7 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
||||||
|
|
||||||
multiStaticSelect(element: IElement, context: BlockContext) {
|
multiStaticSelect(element: IElement, context: BlockContext) {
|
||||||
const [{ loading, value }, action] = useBlockContext(element, context);
|
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||||
const valueFiltered = element.options?.filter(option => value.includes(option.value));
|
const valueFiltered = element?.options?.filter(option => value?.includes(option.value));
|
||||||
return <MultiSelect {...element} value={valueFiltered} onChange={action} context={context} loading={loading} multiselect />;
|
return <MultiSelect {...element} value={valueFiltered} onChange={action} context={context} loading={loading} multiselect />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ const Thread = React.memo(
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
|
const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
|
||||||
|
|
||||||
if (!tlm || isThreadRoom || tcount === 0) {
|
if (!tlm || isThreadRoom || tcount === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export const STATUSES = ['offline', 'online', 'away', 'busy', 'disabled'] as const;
|
export const STATUSES = ['offline', 'online', 'away', 'busy', 'disabled', 'loading'] as const;
|
||||||
|
|
||||||
export type TUserStatus = typeof STATUSES[number];
|
export type TUserStatus = typeof STATUSES[number];
|
||||||
|
|
|
@ -3,6 +3,6 @@ export type SubscriptionsEndpoints = {
|
||||||
POST: (params: { firstUnreadMessage: { _id: string } } | { roomId: string }) => {};
|
POST: (params: { firstUnreadMessage: { _id: string } } | { roomId: string }) => {};
|
||||||
};
|
};
|
||||||
'subscriptions.read': {
|
'subscriptions.read': {
|
||||||
POST: (params: { rid: string }) => {};
|
POST: (params: { rid: string; readThreads?: boolean }) => {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -555,7 +555,6 @@
|
||||||
"Enabled_E2E_Encryption_for_this_room": "enabled E2E encryption for this room",
|
"Enabled_E2E_Encryption_for_this_room": "enabled E2E encryption for this room",
|
||||||
"Disabled_E2E_Encryption_for_this_room": "disabled E2E encryption for this room",
|
"Disabled_E2E_Encryption_for_this_room": "disabled E2E encryption for this room",
|
||||||
"Teams": "Teams",
|
"Teams": "Teams",
|
||||||
"No_team_channels_found": "No channels found",
|
|
||||||
"Team_not_found": "Team not found",
|
"Team_not_found": "Team not found",
|
||||||
"Create_Team": "Create team",
|
"Create_Team": "Create team",
|
||||||
"Team_Name": "Team name",
|
"Team_Name": "Team name",
|
||||||
|
@ -744,5 +743,6 @@
|
||||||
"accept": "Accept",
|
"accept": "Accept",
|
||||||
"Incoming_call_from": "Incoming call from",
|
"Incoming_call_from": "Incoming call from",
|
||||||
"Call_started": "Call started",
|
"Call_started": "Call started",
|
||||||
"Message_has_been_shared":"Message has been shared"
|
"Message_has_been_shared":"Message has been shared",
|
||||||
|
"No_channels_in_team": "No Channels on this team"
|
||||||
}
|
}
|
|
@ -731,5 +731,6 @@
|
||||||
"Select": "Selecionar",
|
"Select": "Selecionar",
|
||||||
"Nickname": "Apelido",
|
"Nickname": "Apelido",
|
||||||
"Bio": "Biografia",
|
"Bio": "Biografia",
|
||||||
"Message_has_been_shared":"Menssagem foi compartilhada"
|
"Message_has_been_shared":"Menssagem foi compartilhada",
|
||||||
|
"No_channels_in_team": "Nenhum canal nesta equipe"
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-c
|
||||||
import RNScreens from 'react-native-screens';
|
import RNScreens from 'react-native-screens';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
|
import Orientation from 'react-native-orientation-locker';
|
||||||
|
|
||||||
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
||||||
import { deepLinkingOpen } from './actions/deepLinking';
|
import { deepLinkingOpen } from './actions/deepLinking';
|
||||||
|
@ -104,6 +105,9 @@ export default class Root extends React.Component<{}, IState> {
|
||||||
};
|
};
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
this.initTablet();
|
this.initTablet();
|
||||||
|
Orientation.unlockAllOrientations();
|
||||||
|
} else {
|
||||||
|
Orientation.lockToPortrait();
|
||||||
}
|
}
|
||||||
setNativeTheme(theme);
|
setNativeTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ export const colors = {
|
||||||
headerTintColor: '#f9f9f9',
|
headerTintColor: '#f9f9f9',
|
||||||
headerTitleColor: '#f9f9f9',
|
headerTitleColor: '#f9f9f9',
|
||||||
headerSecondaryText: '#9297a2',
|
headerSecondaryText: '#9297a2',
|
||||||
toastBackground: '#0C0D0F',
|
toastBackground: '#54585e',
|
||||||
videoBackground: '#1f2329',
|
videoBackground: '#1f2329',
|
||||||
favoriteBackground: '#ffbb00',
|
favoriteBackground: '#ffbb00',
|
||||||
hideBackground: '#54585e',
|
hideBackground: '#54585e',
|
||||||
|
@ -228,7 +228,7 @@ export const colors = {
|
||||||
headerTintColor: '#f9f9f9',
|
headerTintColor: '#f9f9f9',
|
||||||
headerTitleColor: '#f9f9f9',
|
headerTitleColor: '#f9f9f9',
|
||||||
headerSecondaryText: '#b2b8c6',
|
headerSecondaryText: '#b2b8c6',
|
||||||
toastBackground: '#0C0D0F',
|
toastBackground: '#54585e',
|
||||||
videoBackground: '#1f2329',
|
videoBackground: '#1f2329',
|
||||||
favoriteBackground: '#ffbb00',
|
favoriteBackground: '#ffbb00',
|
||||||
hideBackground: '#54585e',
|
hideBackground: '#54585e',
|
||||||
|
|
|
@ -102,6 +102,9 @@ export const defaultSettings = {
|
||||||
E2E_Enable: {
|
E2E_Enable: {
|
||||||
type: 'valueAsBoolean'
|
type: 'valueAsBoolean'
|
||||||
},
|
},
|
||||||
|
E2E_Enabled_Default_PrivateRooms: {
|
||||||
|
type: 'valueAsBoolean'
|
||||||
|
},
|
||||||
Accounts_Directory_DefaultView: {
|
Accounts_Directory_DefaultView: {
|
||||||
type: 'valueAsString'
|
type: 'valueAsString'
|
||||||
},
|
},
|
||||||
|
|
|
@ -199,10 +199,16 @@ class Encryption {
|
||||||
|
|
||||||
// Encode the private key
|
// Encode the private key
|
||||||
const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId as string);
|
const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId as string);
|
||||||
|
|
||||||
|
// This public key is already encoded using EJSON.stringify in the `persistKeys` method
|
||||||
const publicKey = UserPreferences.getString(`${server}-${E2E_PUBLIC_KEY}`);
|
const publicKey = UserPreferences.getString(`${server}-${E2E_PUBLIC_KEY}`);
|
||||||
|
|
||||||
|
if (!publicKey) {
|
||||||
|
throw new Error('Public key not found in local storage, password not changed');
|
||||||
|
}
|
||||||
|
|
||||||
// Send the new keys to the server
|
// Send the new keys to the server
|
||||||
await Services.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey);
|
await Services.e2eSetUserPublicAndPrivateKeys(publicKey, encodedPrivateKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
// get a encryption room instance
|
// get a encryption room instance
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { StatusBar } from 'react-native';
|
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
||||||
|
|
||||||
import { isIOS, isTablet } from '../methods/helpers';
|
|
||||||
|
|
||||||
// Not sure if it's worth adding this here in the context of the actionSheet
|
|
||||||
/**
|
|
||||||
* Return the snaps based on the size you pass (aka: Size of action sheet)
|
|
||||||
* @param {number} componentSize size of the component that will be rendered in the action sheet
|
|
||||||
*/
|
|
||||||
export const useSnaps = (componentSize: number): number[] | string[] => {
|
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
if (isIOS) {
|
|
||||||
const fixTabletInset = isTablet ? 2 : 1;
|
|
||||||
return [componentSize + (insets.bottom || insets.top) * fixTabletInset];
|
|
||||||
}
|
|
||||||
let statusHeight = 0;
|
|
||||||
if (StatusBar.currentHeight) {
|
|
||||||
statusHeight = StatusBar.currentHeight;
|
|
||||||
}
|
|
||||||
return [componentSize + statusHeight];
|
|
||||||
};
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Camera, CameraType } from 'expo-camera';
|
import { Camera, CameraType } from 'expo-camera';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { useAppSelector } from '..';
|
import { useAppSelector } from '..';
|
||||||
|
@ -13,7 +12,6 @@ import Ringer, { ERingerSounds } from '../../../containers/Ringer';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import { getUserSelector } from '../../../selectors/login';
|
import { getUserSelector } from '../../../selectors/login';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import { isIOS } from '../../methods/helpers';
|
|
||||||
import useUserData from '../useUserData';
|
import useUserData from '../useUserData';
|
||||||
|
|
||||||
export default function StartACallActionSheet({ rid }: { rid: string }): React.ReactElement {
|
export default function StartACallActionSheet({ rid }: { rid: string }): React.ReactElement {
|
||||||
|
@ -28,10 +26,6 @@ export default function StartACallActionSheet({ rid }: { rid: string }): React.R
|
||||||
|
|
||||||
const user = useUserData(rid);
|
const user = useUserData(rid);
|
||||||
|
|
||||||
// fix safe area bottom padding on iOS
|
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
const paddingBottom = isIOS && insets.bottom ? 8 : 0;
|
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
if (calling) {
|
if (calling) {
|
||||||
|
@ -42,10 +36,7 @@ export default function StartACallActionSheet({ rid }: { rid: string }): React.R
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View style={style.actionSheetContainer} onLayout={e => setContainerWidth(e.nativeEvent.layout.width / 2)}>
|
||||||
style={[style.actionSheetContainer, { paddingBottom }]}
|
|
||||||
onLayout={e => setContainerWidth(e.nativeEvent.layout.width / 2)}
|
|
||||||
>
|
|
||||||
{calling ? <Ringer ringer={ERingerSounds.DIALTONE} /> : null}
|
{calling ? <Ringer ringer={ERingerSounds.DIALTONE} /> : null}
|
||||||
<CallHeader
|
<CallHeader
|
||||||
title={calling && user.direct ? i18n.t('Calling') : i18n.t('Start_a_call')}
|
title={calling && user.direct ? i18n.t('Calling') : i18n.t('Start_a_call')}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { Camera } from 'expo-camera';
|
import { Camera } from 'expo-camera';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { useActionSheet } from '../../../containers/ActionSheet';
|
import { useActionSheet } from '../../../containers/ActionSheet';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
import { getUserSelector } from '../../../selectors/login';
|
import { getUserSelector } from '../../../selectors/login';
|
||||||
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
||||||
|
import log from '../../methods/helpers/log';
|
||||||
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
||||||
import { Services } from '../../services';
|
import { Services } from '../../services';
|
||||||
import { useAppSelector } from '../useAppSelector';
|
import { useAppSelector } from '../useAppSelector';
|
||||||
import { useSnaps } from '../useSnaps';
|
|
||||||
import StartACallActionSheet from './StartACallActionSheet';
|
import StartACallActionSheet from './StartACallActionSheet';
|
||||||
import { useVideoConfCall } from './useVideoConfCall';
|
import { useVideoConfCall } from './useVideoConfCall';
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ const availabilityErrors = {
|
||||||
NO_APP: 'no-videoconf-provider-app'
|
NO_APP: 'no-videoconf-provider-app'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const handleErrors = (isAdmin: boolean, error: typeof availabilityErrors[keyof typeof availabilityErrors]) => {
|
const handleErrors = (isAdmin: boolean, error: keyof typeof availabilityErrors) => {
|
||||||
if (isAdmin) return showErrorAlert(i18n.t(`admin-${error}-body`), i18n.t(`admin-${error}-header`));
|
const key = isAdmin ? `admin-${error}` : error;
|
||||||
return showErrorAlert(i18n.t(`${error}-body`), i18n.t(`${error}-header`));
|
showErrorAlert(i18n.t(`${key}-body`), i18n.t(`${key}-header`));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useVideoConf = (
|
export const useVideoConf = (
|
||||||
|
@ -32,48 +32,42 @@ export const useVideoConf = (
|
||||||
const { callEnabled, disabledTooltip } = useVideoConfCall(rid);
|
const { callEnabled, disabledTooltip } = useVideoConfCall(rid);
|
||||||
|
|
||||||
const [permission, requestPermission] = Camera.useCameraPermissions();
|
const [permission, requestPermission] = Camera.useCameraPermissions();
|
||||||
|
|
||||||
const { showActionSheet } = useActionSheet();
|
const { showActionSheet } = useActionSheet();
|
||||||
const snaps = useSnaps(404);
|
|
||||||
|
|
||||||
const isServer5OrNewer = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0');
|
const isServer5OrNewer = useMemo(() => compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.0.0'), [serverVersion]);
|
||||||
|
|
||||||
const canInitAnCall = async () => {
|
const canInitAnCall = async (): Promise<boolean> => {
|
||||||
if (callEnabled) {
|
if (!callEnabled) return false;
|
||||||
if (isServer5OrNewer) {
|
|
||||||
try {
|
if (isServer5OrNewer) {
|
||||||
await Services.videoConferenceGetCapabilities();
|
try {
|
||||||
return true;
|
await Services.videoConferenceGetCapabilities();
|
||||||
} catch (error: any) {
|
return true;
|
||||||
const isAdmin = !!user.roles?.includes('admin');
|
} catch (error: any) {
|
||||||
switch (error?.error) {
|
const isAdmin = !!user.roles?.includes('admin');
|
||||||
case availabilityErrors.NOT_CONFIGURED:
|
handleErrors(isAdmin, error?.error || 'NOT_CONFIGURED');
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
return false;
|
||||||
case availabilityErrors.NOT_ACTIVE:
|
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_ACTIVE);
|
|
||||||
case availabilityErrors.NO_APP:
|
|
||||||
return handleErrors(isAdmin, availabilityErrors.NO_APP);
|
|
||||||
default:
|
|
||||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showInitCallActionSheet = async () => {
|
const showInitCallActionSheet = async () => {
|
||||||
const canInit = await canInitAnCall();
|
try {
|
||||||
if (canInit) {
|
const canInit = await canInitAnCall();
|
||||||
showActionSheet({
|
if (canInit) {
|
||||||
children: <StartACallActionSheet rid={rid} />,
|
showActionSheet({
|
||||||
snaps
|
children: <StartACallActionSheet rid={rid} />,
|
||||||
});
|
snaps: [480]
|
||||||
if (!permission?.granted) {
|
});
|
||||||
requestPermission();
|
|
||||||
handleAndroidBltPermission();
|
if (!permission?.granted) {
|
||||||
|
requestPermission();
|
||||||
|
handleAndroidBltPermission();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ const serverInfoKeys = [
|
||||||
'Force_Screen_Lock',
|
'Force_Screen_Lock',
|
||||||
'Force_Screen_Lock_After',
|
'Force_Screen_Lock_After',
|
||||||
'uniqueID',
|
'uniqueID',
|
||||||
'E2E_Enable'
|
'E2E_Enable',
|
||||||
|
'E2E_Enabled_Default_PrivateRooms'
|
||||||
];
|
];
|
||||||
|
|
||||||
// these settings are used only on onboarding process
|
// these settings are used only on onboarding process
|
||||||
|
@ -85,6 +86,9 @@ const serverInfoUpdate = async (serverInfo: IPreparedSettings[], iconSetting: IS
|
||||||
if (setting._id === 'E2E_Enable') {
|
if (setting._id === 'E2E_Enable') {
|
||||||
return { ...allSettings, E2E_Enable: setting.valueAsBoolean };
|
return { ...allSettings, E2E_Enable: setting.valueAsBoolean };
|
||||||
}
|
}
|
||||||
|
if (setting._id === 'E2E_Enabled_Default_PrivateRooms') {
|
||||||
|
return { ...allSettings, E2E_Enabled_Default_PrivateRooms: setting.valueAsBoolean };
|
||||||
|
}
|
||||||
return allSettings;
|
return allSettings;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
|
import { URL } from 'react-native-url-polyfill';
|
||||||
|
|
||||||
import { LOCAL_DOCUMENT_DIRECTORY } from '../handleMediaDownload';
|
import { LOCAL_DOCUMENT_DIRECTORY } from '../handleMediaDownload';
|
||||||
|
|
||||||
|
function setParamInUrl({ url, token, userId }: { url: string; token: string; userId: string }) {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
urlObj.searchParams.set('rc_token', token);
|
||||||
|
urlObj.searchParams.set('rc_uid', userId);
|
||||||
|
return urlObj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => {
|
export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => {
|
||||||
if (LOCAL_DOCUMENT_DIRECTORY && attachmentUrl?.startsWith(LOCAL_DOCUMENT_DIRECTORY)) {
|
if (LOCAL_DOCUMENT_DIRECTORY && attachmentUrl?.startsWith(LOCAL_DOCUMENT_DIRECTORY)) {
|
||||||
return attachmentUrl;
|
return attachmentUrl;
|
||||||
|
@ -8,7 +17,7 @@ export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: s
|
||||||
if (attachmentUrl.includes('rc_token')) {
|
if (attachmentUrl.includes('rc_token')) {
|
||||||
return encodeURI(attachmentUrl);
|
return encodeURI(attachmentUrl);
|
||||||
}
|
}
|
||||||
return encodeURI(`${attachmentUrl}?rc_uid=${userId}&rc_token=${token}`);
|
return setParamInUrl({ url: attachmentUrl, token, userId });
|
||||||
}
|
}
|
||||||
return encodeURI(`${server}${attachmentUrl}?rc_uid=${userId}&rc_token=${token}`);
|
return setParamInUrl({ url: `${server}${attachmentUrl}`, token, userId });
|
||||||
};
|
};
|
||||||
|
|
|
@ -254,6 +254,8 @@ const debouncedUpdate = (subscription: ISubscription) => {
|
||||||
if (batch[key]) {
|
if (batch[key]) {
|
||||||
if (/SUB/.test(key)) {
|
if (/SUB/.test(key)) {
|
||||||
const sub = batch[key] as ISubscription;
|
const sub = batch[key] as ISubscription;
|
||||||
|
// When calling the api subscriptions.read passing readThreads as true it does not return this prop
|
||||||
|
if (!sub.tunread) sub.tunread = [];
|
||||||
const roomQueueId = getRoomQueueId(sub.rid);
|
const roomQueueId = getRoomQueueId(sub.rid);
|
||||||
const room = batch[roomQueueId] as IRoom;
|
const room = batch[roomQueueId] as IRoom;
|
||||||
delete batch[roomQueueId];
|
delete batch[roomQueueId];
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
import {
|
import {
|
||||||
|
IAvatarSuggestion,
|
||||||
IMessage,
|
IMessage,
|
||||||
INotificationPreferences,
|
INotificationPreferences,
|
||||||
IPreviewItem,
|
IPreviewItem,
|
||||||
|
IProfileParams,
|
||||||
IRoom,
|
IRoom,
|
||||||
IRoomNotifications,
|
IRoomNotifications,
|
||||||
SubscriptionType,
|
IServerRoom,
|
||||||
IUser,
|
IUser,
|
||||||
IAvatarSuggestion,
|
|
||||||
IProfileParams,
|
|
||||||
RoomType,
|
RoomType,
|
||||||
IServerRoom
|
SubscriptionType
|
||||||
} from '../../definitions';
|
} from '../../definitions';
|
||||||
|
import { TParams } from '../../definitions/ILivechatEditView';
|
||||||
|
import { ILivechatTag } from '../../definitions/ILivechatTag';
|
||||||
import { ISpotlight } from '../../definitions/ISpotlight';
|
import { ISpotlight } from '../../definitions/ISpotlight';
|
||||||
import { TEAM_TYPE } from '../../definitions/ITeam';
|
import { TEAM_TYPE } from '../../definitions/ITeam';
|
||||||
|
import { OperationParams, ResultFor } from '../../definitions/rest/helpers';
|
||||||
|
import { SubscriptionsEndpoints } from '../../definitions/rest/v1/subscriptions';
|
||||||
import { Encryption } from '../encryption';
|
import { Encryption } from '../encryption';
|
||||||
import { TParams } from '../../definitions/ILivechatEditView';
|
|
||||||
import { store as reduxStore } from '../store/auxStore';
|
|
||||||
import { getDeviceToken } from '../notifications';
|
|
||||||
import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods';
|
import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods';
|
||||||
import sdk from './sdk';
|
|
||||||
import { compareServerVersion, getBundleId, isIOS } from '../methods/helpers';
|
import { compareServerVersion, getBundleId, isIOS } from '../methods/helpers';
|
||||||
import { ILivechatTag } from '../../definitions/ILivechatTag';
|
import { getDeviceToken } from '../notifications';
|
||||||
|
import { store as reduxStore } from '../store/auxStore';
|
||||||
|
import sdk from './sdk';
|
||||||
|
|
||||||
export const createChannel = ({
|
export const createChannel = ({
|
||||||
name,
|
name,
|
||||||
|
@ -310,11 +312,33 @@ export const setReaction = (emoji: string, messageId: string) =>
|
||||||
// RC 0.62.2
|
// RC 0.62.2
|
||||||
sdk.post('chat.react', { emoji, messageId });
|
sdk.post('chat.react', { emoji, messageId });
|
||||||
|
|
||||||
export const toggleRead = (read: boolean, roomId: string) => {
|
/**
|
||||||
if (read) {
|
* Toggles the read status of a room.
|
||||||
return sdk.post('subscriptions.unread', { roomId });
|
*
|
||||||
|
* @param isRead - Whether to mark the room as read or unread.
|
||||||
|
* @param roomId - The ID of the room.
|
||||||
|
* @param includeThreads - Optional flag to include threads when marking as read.
|
||||||
|
* @returns A promise from the sdk post method.
|
||||||
|
*/
|
||||||
|
export const toggleReadStatus = (
|
||||||
|
isRead: boolean,
|
||||||
|
roomId: string,
|
||||||
|
includeThreads?: boolean
|
||||||
|
): Promise<ResultFor<'POST', keyof SubscriptionsEndpoints>> => {
|
||||||
|
let endpoint: keyof SubscriptionsEndpoints;
|
||||||
|
let payload: OperationParams<'POST', keyof SubscriptionsEndpoints> = { roomId };
|
||||||
|
|
||||||
|
if (isRead) {
|
||||||
|
endpoint = 'subscriptions.unread';
|
||||||
|
} else {
|
||||||
|
endpoint = 'subscriptions.read';
|
||||||
|
payload = { rid: roomId };
|
||||||
|
if (includeThreads) {
|
||||||
|
payload.readThreads = includeThreads;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return sdk.post('subscriptions.read', { rid: roomId });
|
|
||||||
|
return sdk.post(endpoint, payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRoomCounters = (
|
export const getRoomCounters = (
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
import useDeepCompareEffect from 'use-deep-compare-effect';
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { hasNotch, isTablet } from '../lib/methods/helpers';
|
import { hasNotch } from '../lib/methods/helpers';
|
||||||
import { PasscodeChoose } from '../containers/Passcode';
|
import { PasscodeChoose } from '../containers/Passcode';
|
||||||
import EventEmitter from '../lib/methods/helpers/events';
|
import EventEmitter from '../lib/methods/helpers/events';
|
||||||
import { CustomIcon } from '../containers/CustomIcon';
|
import { CustomIcon } from '../containers/CustomIcon';
|
||||||
|
@ -65,14 +64,8 @@ const ChangePasscodeView = React.memo(() => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.lockToPortrait();
|
|
||||||
}
|
|
||||||
const listener = EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
|
const listener = EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
|
||||||
return () => {
|
return () => {
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.unlockAllOrientations();
|
|
||||||
}
|
|
||||||
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER, listener);
|
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER, listener);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -13,16 +13,18 @@ export const RoomSettings = ({
|
||||||
isTeam,
|
isTeam,
|
||||||
setValue,
|
setValue,
|
||||||
createChannelPermission,
|
createChannelPermission,
|
||||||
createPrivateChannelPermission
|
createPrivateChannelPermission,
|
||||||
|
e2eEnabledDefaultPrivateRooms
|
||||||
}: {
|
}: {
|
||||||
isTeam: boolean;
|
isTeam: boolean;
|
||||||
setValue: UseFormSetValue<IFormData>;
|
setValue: UseFormSetValue<IFormData>;
|
||||||
createChannelPermission: boolean;
|
createChannelPermission: boolean;
|
||||||
createPrivateChannelPermission: boolean;
|
createPrivateChannelPermission: boolean;
|
||||||
|
e2eEnabledDefaultPrivateRooms: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [type, setType] = useState(true);
|
const [type, setType] = useState(true);
|
||||||
const [readOnly, setReadOnly] = useState(false);
|
const [readOnly, setReadOnly] = useState(false);
|
||||||
const [encrypted, setEncrypted] = useState(false);
|
const [encrypted, setEncrypted] = useState(e2eEnabledDefaultPrivateRooms);
|
||||||
const [broadcast, setBroadcast] = useState(false);
|
const [broadcast, setBroadcast] = useState(false);
|
||||||
|
|
||||||
const { encryptionEnabled } = useAppSelector(state => ({
|
const { encryptionEnabled } = useAppSelector(state => ({
|
||||||
|
|
|
@ -68,13 +68,29 @@ export interface IFormData {
|
||||||
const CreateChannelView = () => {
|
const CreateChannelView = () => {
|
||||||
const [createChannelPermission, createPrivateChannelPermission] = usePermissions(['create-c', 'create-p']);
|
const [createChannelPermission, createPrivateChannelPermission] = usePermissions(['create-c', 'create-p']);
|
||||||
|
|
||||||
|
const { isFetching, useRealName, users, e2eEnabledDefaultPrivateRooms } = useAppSelector(
|
||||||
|
state => ({
|
||||||
|
isFetching: state.createChannel.isFetching,
|
||||||
|
users: state.selectedUsers.users,
|
||||||
|
useRealName: state.settings.UI_Use_Real_Name as boolean,
|
||||||
|
e2eEnabledDefaultPrivateRooms: state.encryption.enabled && (state.settings.E2E_Enabled_Default_PrivateRooms as boolean)
|
||||||
|
}),
|
||||||
|
shallowEqual
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { isDirty },
|
formState: { isDirty },
|
||||||
setValue
|
setValue
|
||||||
} = useForm<IFormData>({
|
} = useForm<IFormData>({
|
||||||
defaultValues: { channelName: '', broadcast: false, encrypted: false, readOnly: false, type: createPrivateChannelPermission }
|
defaultValues: {
|
||||||
|
channelName: '',
|
||||||
|
broadcast: false,
|
||||||
|
encrypted: e2eEnabledDefaultPrivateRooms,
|
||||||
|
readOnly: false,
|
||||||
|
type: createPrivateChannelPermission
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'CreateChannelView'>>();
|
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'CreateChannelView'>>();
|
||||||
|
@ -84,15 +100,6 @@ const CreateChannelView = () => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const { isFetching, useRealName, users } = useAppSelector(
|
|
||||||
state => ({
|
|
||||||
isFetching: state.createChannel.isFetching,
|
|
||||||
users: state.selectedUsers.users,
|
|
||||||
useRealName: state.settings.UI_Use_Real_Name as boolean
|
|
||||||
}),
|
|
||||||
shallowEqual
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sendLoadingEvent({ visible: isFetching });
|
sendLoadingEvent({ visible: isFetching });
|
||||||
}, [isFetching]);
|
}, [isFetching]);
|
||||||
|
@ -154,6 +161,7 @@ const CreateChannelView = () => {
|
||||||
createPrivateChannelPermission={createPrivateChannelPermission}
|
createPrivateChannelPermission={createPrivateChannelPermission}
|
||||||
isTeam={isTeam}
|
isTeam={isTeam}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
|
e2eEnabledDefaultPrivateRooms={e2eEnabledDefaultPrivateRooms}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{users.length > 0 ? (
|
{users.length > 0 ? (
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Base64 } from 'js-base64';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BackHandler, Image, Keyboard, StyleSheet, Text, View } from 'react-native';
|
import { BackHandler, Image, Keyboard, StyleSheet, Text, View } from 'react-native';
|
||||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import parse from 'url-parse';
|
import parse from 'url-parse';
|
||||||
|
|
||||||
|
@ -88,9 +87,6 @@ interface ISubmitParams {
|
||||||
class NewServerView extends React.Component<INewServerViewProps, INewServerViewState> {
|
class NewServerView extends React.Component<INewServerViewProps, INewServerViewState> {
|
||||||
constructor(props: INewServerViewProps) {
|
constructor(props: INewServerViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.lockToPortrait();
|
|
||||||
}
|
|
||||||
this.setHeader();
|
this.setHeader();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { sha256 } from 'js-sha256';
|
import { sha256 } from 'js-sha256';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Keyboard, Text } from 'react-native';
|
import { Keyboard, Text } from 'react-native';
|
||||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { deleteAccount } from '../../../../actions/login';
|
import { deleteAccount } from '../../../../actions/login';
|
||||||
|
@ -18,7 +17,6 @@ import sharedStyles from '../../../Styles';
|
||||||
export function DeleteAccountActionSheetContent(): React.ReactElement {
|
export function DeleteAccountActionSheetContent(): React.ReactElement {
|
||||||
const { hideActionSheet, showActionSheet } = useActionSheet();
|
const { hideActionSheet, showActionSheet } = useActionSheet();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const insets = useSafeAreaInsets();
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
const handleDeleteAccount = async (password: string) => {
|
const handleDeleteAccount = async (password: string) => {
|
||||||
|
@ -40,8 +38,7 @@ export function DeleteAccountActionSheetContent(): React.ReactElement {
|
||||||
removedRooms={removedRooms}
|
removedRooms={removedRooms}
|
||||||
password={sha256(password)}
|
password={sha256(password)}
|
||||||
/>
|
/>
|
||||||
),
|
)
|
||||||
headerHeight: 225 + insets.bottom
|
|
||||||
});
|
});
|
||||||
}, 250); // timeout for hide effect
|
}, 250); // timeout for hide effect
|
||||||
} else if (error.data.errorType === 'error-invalid-password') {
|
} else if (error.data.errorType === 'error-invalid-password') {
|
||||||
|
|
|
@ -237,8 +237,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
}}
|
}}
|
||||||
onCancel={this.props.hideActionSheet}
|
onCancel={this.props.hideActionSheet}
|
||||||
/>
|
/>
|
||||||
),
|
)
|
||||||
headerHeight: 225
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -417,8 +416,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
deleteOwnAccount = () => {
|
deleteOwnAccount = () => {
|
||||||
logEvent(events.DELETE_OWN_ACCOUNT);
|
logEvent(events.DELETE_OWN_ACCOUNT);
|
||||||
this.props.showActionSheet({
|
this.props.showActionSheet({
|
||||||
children: <DeleteAccountActionSheetContent />,
|
children: <DeleteAccountActionSheetContent />
|
||||||
headerHeight: 225
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1109,7 +1109,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
||||||
{teamId && isTeamRoom({ teamId, joined }) ? (
|
{teamId && isTeamRoom({ teamId, joined }) ? (
|
||||||
<>
|
<>
|
||||||
<List.Item
|
<List.Item
|
||||||
title='Teams'
|
title='Channels'
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
|
|
|
@ -857,7 +857,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
children: (
|
children: (
|
||||||
<ReactionPicker message={selectedMessage} onEmojiSelected={this.onReactionPress} reactionClose={this.onReactionClose} />
|
<ReactionPicker message={selectedMessage} onEmojiSelected={this.onReactionPress} reactionClose={this.onReactionClose} />
|
||||||
),
|
),
|
||||||
snaps: [400],
|
snaps: ['50%'],
|
||||||
enableContentPanningGesture: false
|
enableContentPanningGesture: false
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
|
@ -4,8 +4,6 @@ import { StyleSheet, Text, TextInputProps, TouchableOpacity, TouchableOpacityPro
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
import sharedStyles from '../../Styles';
|
import sharedStyles from '../../Styles';
|
||||||
import { CustomIcon } from '../../../containers/CustomIcon';
|
import { CustomIcon } from '../../../containers/CustomIcon';
|
||||||
import { isIOS, isTablet } from '../../../lib/methods/helpers';
|
|
||||||
import { useOrientation } from '../../../dimensions';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import SearchHeader from '../../../containers/SearchHeader';
|
import SearchHeader from '../../../containers/SearchHeader';
|
||||||
|
|
||||||
|
@ -20,9 +18,11 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
|
fontSize: 16,
|
||||||
...sharedStyles.textSemibold
|
...sharedStyles.textSemibold
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
|
fontSize: 14,
|
||||||
...sharedStyles.textRegular
|
...sharedStyles.textRegular
|
||||||
},
|
},
|
||||||
upsideDown: {
|
upsideDown: {
|
||||||
|
@ -55,10 +55,6 @@ const Header = React.memo(
|
||||||
onPress
|
onPress
|
||||||
}: IRoomHeader) => {
|
}: IRoomHeader) => {
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
const { isLandscape } = useOrientation();
|
|
||||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
|
||||||
const titleFontSize = 16 * scale;
|
|
||||||
const subTitleFontSize = 14 * scale;
|
|
||||||
|
|
||||||
if (showSearchHeader) {
|
if (showSearchHeader) {
|
||||||
return <SearchHeader onSearchChangeText={onSearchChangeText} testID='rooms-list-view-search-input' />;
|
return <SearchHeader onSearchChangeText={onSearchChangeText} testID='rooms-list-view-search-input' />;
|
||||||
|
@ -77,7 +73,7 @@ const Header = React.memo(
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TouchableOpacity onPress={onPress} testID='rooms-list-header-server-dropdown-button'>
|
<TouchableOpacity onPress={onPress} testID='rooms-list-header-server-dropdown-button'>
|
||||||
<View style={styles.button}>
|
<View style={styles.button}>
|
||||||
<Text style={[styles.title, { fontSize: titleFontSize, color: colors.headerTitleColor }]} numberOfLines={1}>
|
<Text style={[styles.title, { color: colors.headerTitleColor }]} numberOfLines={1}>
|
||||||
{serverName}
|
{serverName}
|
||||||
</Text>
|
</Text>
|
||||||
<CustomIcon
|
<CustomIcon
|
||||||
|
@ -90,7 +86,7 @@ const Header = React.memo(
|
||||||
{subtitle ? (
|
{subtitle ? (
|
||||||
<Text
|
<Text
|
||||||
testID='rooms-list-header-server-subtitle'
|
testID='rooms-list-header-server-subtitle'
|
||||||
style={[styles.subtitle, { color: colors.auxiliaryText, fontSize: subTitleFontSize }]}
|
style={[styles.subtitle, { color: colors.auxiliaryText }]}
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
>
|
>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { BackHandler, FlatList, Keyboard, NativeEventSubscription, RefreshControl, Text, View } from 'react-native';
|
import { BackHandler, FlatList, Keyboard, NativeEventSubscription, RefreshControl, Text, View } from 'react-native';
|
||||||
import { batch, connect } from 'react-redux';
|
import { batch, connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
@ -56,7 +55,8 @@ import {
|
||||||
isRead,
|
isRead,
|
||||||
debounce,
|
debounce,
|
||||||
isIOS,
|
isIOS,
|
||||||
isTablet
|
isTablet,
|
||||||
|
compareServerVersion
|
||||||
} from '../../lib/methods/helpers';
|
} from '../../lib/methods/helpers';
|
||||||
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
|
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
|
@ -104,6 +104,7 @@ interface IRoomsListViewProps {
|
||||||
createPublicChannelPermission: [];
|
createPublicChannelPermission: [];
|
||||||
createPrivateChannelPermission: [];
|
createPrivateChannelPermission: [];
|
||||||
createDiscussionPermission: [];
|
createDiscussionPermission: [];
|
||||||
|
serverVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRoomsListViewState {
|
interface IRoomsListViewState {
|
||||||
|
@ -216,7 +217,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
|
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
|
||||||
}
|
}
|
||||||
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||||
Orientation.unlockAllOrientations();
|
|
||||||
this.animated = true;
|
this.animated = true;
|
||||||
// Check if there were changes with sort preference, then call getSubscription to remount the list
|
// Check if there were changes with sort preference, then call getSubscription to remount the list
|
||||||
if (this.sortPreferencesChanged) {
|
if (this.sortPreferencesChanged) {
|
||||||
|
@ -706,9 +706,11 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
|
|
||||||
toggleRead = async (rid: string, tIsRead: boolean) => {
|
toggleRead = async (rid: string, tIsRead: boolean) => {
|
||||||
logEvent(tIsRead ? events.RL_UNREAD_CHANNEL : events.RL_READ_CHANNEL);
|
logEvent(tIsRead ? events.RL_UNREAD_CHANNEL : events.RL_READ_CHANNEL);
|
||||||
|
const { serverVersion } = this.props;
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const result = await Services.toggleRead(tIsRead, rid);
|
const includeThreads = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '5.4.0');
|
||||||
|
const result = await Services.toggleReadStatus(tIsRead, rid, includeThreads);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const subCollection = db.get('subscriptions');
|
const subCollection = db.get('subscriptions');
|
||||||
|
@ -718,6 +720,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
await subRecord.update(sub => {
|
await subRecord.update(sub => {
|
||||||
sub.alert = tIsRead;
|
sub.alert = tIsRead;
|
||||||
sub.unread = 0;
|
sub.unread = 0;
|
||||||
|
if (includeThreads) {
|
||||||
|
sub.tunread = [];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -955,7 +960,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
||||||
showAvatar,
|
showAvatar,
|
||||||
displayMode
|
displayMode
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const id = getUidDirectMessage(item);
|
const id = item.search && item.t === 'd' ? item._id : getUidDirectMessage(item);
|
||||||
const swipeEnabled = this.isSwipeEnabled(item);
|
const swipeEnabled = this.isSwipeEnabled(item);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1067,7 +1072,8 @@ const mapStateToProps = (state: IApplicationState) => ({
|
||||||
createDirectMessagePermission: state.permissions['create-d'],
|
createDirectMessagePermission: state.permissions['create-d'],
|
||||||
createPublicChannelPermission: state.permissions['create-c'],
|
createPublicChannelPermission: state.permissions['create-c'],
|
||||||
createPrivateChannelPermission: state.permissions['create-p'],
|
createPrivateChannelPermission: state.permissions['create-p'],
|
||||||
createDiscussionPermission: state.permissions['start-discussion']
|
createDiscussionPermission: state.permissions['start-discussion'],
|
||||||
|
serverVersion: state.server.version
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
|
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
|
||||||
|
|
|
@ -2,7 +2,6 @@ import isEmpty from 'lodash/isEmpty';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
import useDeepCompareEffect from 'use-deep-compare-effect';
|
import useDeepCompareEffect from 'use-deep-compare-effect';
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ import { PasscodeEnter } from '../containers/Passcode';
|
||||||
import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants';
|
import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants';
|
||||||
import { CustomIcon } from '../containers/CustomIcon';
|
import { CustomIcon } from '../containers/CustomIcon';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { hasNotch, isTablet } from '../lib/methods/helpers';
|
import { hasNotch } from '../lib/methods/helpers';
|
||||||
import EventEmitter from '../lib/methods/helpers/events';
|
import EventEmitter from '../lib/methods/helpers/events';
|
||||||
|
|
||||||
interface IData {
|
interface IData {
|
||||||
|
@ -46,14 +45,8 @@ const ScreenLockedView = (): JSX.Element => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.lockToPortrait();
|
|
||||||
}
|
|
||||||
const listener = EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock);
|
const listener = EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock);
|
||||||
return () => {
|
return () => {
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.unlockAllOrientations();
|
|
||||||
}
|
|
||||||
EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER, listener);
|
EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER, listener);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { useNavigation } from '@react-navigation/native';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { ScrollView, StyleSheet, Text } from 'react-native';
|
import { ScrollView, StyleSheet, Text } from 'react-native';
|
||||||
import Orientation from 'react-native-orientation-locker';
|
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
@ -18,7 +17,7 @@ import I18n from '../i18n';
|
||||||
import KeyboardView from '../containers/KeyboardView';
|
import KeyboardView from '../containers/KeyboardView';
|
||||||
import { getUserSelector } from '../selectors/login';
|
import { getUserSelector } from '../selectors/login';
|
||||||
import { useTheme } from '../theme';
|
import { useTheme } from '../theme';
|
||||||
import { isTablet, showErrorAlert } from '../lib/methods/helpers';
|
import { showErrorAlert } from '../lib/methods/helpers';
|
||||||
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
|
import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps';
|
||||||
import sharedStyles from './Styles';
|
import sharedStyles from './Styles';
|
||||||
import { Services } from '../lib/services';
|
import { Services } from '../lib/services';
|
||||||
|
@ -56,9 +55,6 @@ const SetUsernameView = () => {
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
navigation.setOptions({ title: server });
|
navigation.setOptions({ title: server });
|
||||||
if (!isTablet) {
|
|
||||||
Orientation.lockToPortrait();
|
|
||||||
}
|
|
||||||
}, [navigation, server]);
|
}, [navigation, server]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -122,9 +122,5 @@ export default StyleSheet.create({
|
||||||
},
|
},
|
||||||
inputLastChild: {
|
inputLastChild: {
|
||||||
marginBottom: 15
|
marginBottom: 15
|
||||||
},
|
|
||||||
notchLandscapeContainer: {
|
|
||||||
marginTop: -34,
|
|
||||||
paddingHorizontal: 30
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -520,10 +520,10 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
|
||||||
return <BackgroundContainer loading />;
|
return <BackgroundContainer loading />;
|
||||||
}
|
}
|
||||||
if (isSearching && !search.length) {
|
if (isSearching && !search.length) {
|
||||||
return <BackgroundContainer text={searchText ? I18n.t('No_team_channels_found') : ''} />;
|
return <BackgroundContainer text={searchText ? I18n.t('No_channels_in_team') : ''} />;
|
||||||
}
|
}
|
||||||
if (!isSearching && !data.length) {
|
if (!isSearching && !data.length) {
|
||||||
return <BackgroundContainer text={I18n.t('No_team_channels_found')} />;
|
return <BackgroundContainer text={I18n.t('No_channels_in_team')} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,18 +9,20 @@ import {
|
||||||
TTextMatcher,
|
TTextMatcher,
|
||||||
tapAndWaitFor,
|
tapAndWaitFor,
|
||||||
navigateToRoom,
|
navigateToRoom,
|
||||||
mockMessage
|
mockMessage,
|
||||||
|
tryTapping
|
||||||
} from '../../helpers/app';
|
} from '../../helpers/app';
|
||||||
import { createRandomRoom, createRandomUser } from '../../helpers/data_setup';
|
import { createRandomRoom, createRandomUser } from '../../helpers/data_setup';
|
||||||
|
|
||||||
describe('Threads', () => {
|
describe('Threads', () => {
|
||||||
let room: string;
|
let room: string;
|
||||||
let textMatcher: TTextMatcher;
|
let textMatcher: TTextMatcher;
|
||||||
|
let alertButtonType: string;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const user = await createRandomUser();
|
const user = await createRandomUser();
|
||||||
({ name: room } = await createRandomRoom(user));
|
({ name: room } = await createRandomRoom(user));
|
||||||
({ textMatcher } = platformTypes[device.getPlatform()]);
|
({ textMatcher, alertButtonType } = platformTypes[device.getPlatform()]);
|
||||||
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
||||||
await navigateToLogin();
|
await navigateToLogin();
|
||||||
await login(user.username, user.password);
|
await login(user.username, user.password);
|
||||||
|
@ -219,6 +221,30 @@ describe('Threads', () => {
|
||||||
|
|
||||||
await tapAndWaitFor(element(by.id(`message-thread-button-${thread}`)), element(by.id(`room-view-title-${thread}`)), 2000);
|
await tapAndWaitFor(element(by.id(`message-thread-button-${thread}`)), element(by.id(`room-view-title-${thread}`)), 2000);
|
||||||
await expect(element(by.id('messagebox-input-thread'))).toHaveText('');
|
await expect(element(by.id('messagebox-input-thread'))).toHaveText('');
|
||||||
|
await tapBack();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create thread delete the message and the thread show the correct number of messages', async () => {
|
||||||
|
thread = await mockMessage('thread-message-count');
|
||||||
|
await element(by[textMatcher](thread)).atIndex(0).tap();
|
||||||
|
await element(by[textMatcher](thread)).atIndex(0).longPress();
|
||||||
|
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
|
||||||
|
await element(by[textMatcher]('Reply in thread')).atIndex(0).tap();
|
||||||
|
await element(by.id('messagebox-input')).replaceText('replied');
|
||||||
|
await element(by.id('messagebox-send-message')).tap();
|
||||||
|
await waitFor(element(by.id(`thread-count-1`)))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(5000);
|
||||||
|
await element(by.id(`message-thread-button-${thread}`)).tap();
|
||||||
|
await tryTapping(element(by[textMatcher]('replied')).atIndex(0), 2000, true);
|
||||||
|
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
|
||||||
|
await sleep(300); // wait for animation
|
||||||
|
await element(by[textMatcher]('Delete')).atIndex(0).tap();
|
||||||
|
await element(by[textMatcher]('Delete').and(by.type(alertButtonType))).tap();
|
||||||
|
await tapBack();
|
||||||
|
await waitFor(element(by.id(`thread-count-0`)))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(5000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
import { by, device, element, expect, waitFor } from 'detox';
|
import { by, device, element, expect, waitFor } from 'detox';
|
||||||
|
|
||||||
import { TTextMatcher, login, navigateToLogin, platformTypes, searchRoom, tapBack, tryTapping } from '../../helpers/app';
|
import { TTextMatcher, login, navigateToLogin, platformTypes, searchRoom, sleep, tapBack, tryTapping } from '../../helpers/app';
|
||||||
import { ITestUser, createRandomRoom, createRandomUser, initApi } from '../../helpers/data_setup';
|
import { ITestUser, createRandomRoom, createRandomUser, initApi } from '../../helpers/data_setup';
|
||||||
import random from '../../helpers/random';
|
import random from '../../helpers/random';
|
||||||
|
|
||||||
const roomId = '64b846e4760e618aa9f91ab7';
|
const roomId = '64b846e4760e618aa9f91ab7';
|
||||||
|
|
||||||
|
function getIndex() {
|
||||||
|
if (device.getPlatform() === 'android') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const sendMessageOnTranslationTestRoom = async (msg: string): Promise<{ user: ITestUser; msgId: string }> => {
|
const sendMessageOnTranslationTestRoom = async (msg: string): Promise<{ user: ITestUser; msgId: string }> => {
|
||||||
const user = await createRandomUser();
|
const user = await createRandomUser();
|
||||||
const api = await initApi(user.username, user.password);
|
const api = await initApi(user.username, user.password);
|
||||||
|
@ -36,22 +43,37 @@ async function navigateToRoom(roomName: string) {
|
||||||
.withTimeout(5000);
|
.withTimeout(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function searchMessage(msg: string, textMatcher: TTextMatcher) {
|
||||||
|
await sleep(1000); // wait for proper load the room
|
||||||
|
await element(by.id('room-view-search')).tap();
|
||||||
|
await waitFor(element(by.id('search-messages-view')))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(5000);
|
||||||
|
await element(by.id('search-message-view-input')).replaceText(msg);
|
||||||
|
await waitFor(element(by[textMatcher](msg)).atIndex(getIndex()))
|
||||||
|
.toExist()
|
||||||
|
.withTimeout(30000);
|
||||||
|
await sleep(1000);
|
||||||
|
await element(by[textMatcher](msg)).atIndex(getIndex()).tap();
|
||||||
|
await sleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
export function waitForVisible(id: string) {
|
export function waitForVisible(id: string) {
|
||||||
return waitFor(element(by.id(id)))
|
return waitFor(element(by.id(id)))
|
||||||
.toBeVisible()
|
.toBeVisible()
|
||||||
.withTimeout(5000);
|
.withTimeout(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForVisibleTextMatcher(msg: string, textMatcher: TTextMatcher) {
|
export function waitForVisibleTextMatcher(msg: string, textMatcher: TTextMatcher) {
|
||||||
return waitFor(element(by[textMatcher](msg)).atIndex(0))
|
return waitFor(element(by[textMatcher](msg)).atIndex(0))
|
||||||
.toExist()
|
.toExist()
|
||||||
.withTimeout(5000);
|
.withTimeout(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForNotVisible(id: string) {
|
export function waitForNotVisible(id: string) {
|
||||||
return waitFor(element(by.id(id)))
|
return waitFor(element(by.id(id)))
|
||||||
.not.toBeVisible()
|
.not.toBeVisible()
|
||||||
.withTimeout(5000);
|
.withTimeout(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Auto Translate', () => {
|
describe('Auto Translate', () => {
|
||||||
|
@ -97,6 +119,7 @@ describe('Auto Translate', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should see old message not translated before enable auto translate', async () => {
|
it('should see old message not translated before enable auto translate', async () => {
|
||||||
|
await searchMessage(oldMessage[languages.default] as string, textMatcher);
|
||||||
await waitForVisibleTextMatcher(oldMessage[languages.default] as string, textMatcher);
|
await waitForVisibleTextMatcher(oldMessage[languages.default] as string, textMatcher);
|
||||||
await waitForVisibleTextMatcher(attachmentMessage[languages.default] as string, textMatcher);
|
await waitForVisibleTextMatcher(attachmentMessage[languages.default] as string, textMatcher);
|
||||||
});
|
});
|
||||||
|
@ -141,6 +164,7 @@ describe('Auto Translate', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should see old message translated after enable auto translate', async () => {
|
it('should see old message translated after enable auto translate', async () => {
|
||||||
|
await searchMessage(oldMessage[languages.default] as string, textMatcher);
|
||||||
await waitForVisibleTextMatcher(oldMessage[languages.translated] as string, textMatcher);
|
await waitForVisibleTextMatcher(oldMessage[languages.translated] as string, textMatcher);
|
||||||
await waitForVisibleTextMatcher(attachmentMessage[languages.translated] as string, textMatcher);
|
await waitForVisibleTextMatcher(attachmentMessage[languages.translated] as string, textMatcher);
|
||||||
});
|
});
|
||||||
|
@ -148,6 +172,7 @@ describe('Auto Translate', () => {
|
||||||
it('should see new message translated', async () => {
|
it('should see new message translated', async () => {
|
||||||
const randomMatcher = random();
|
const randomMatcher = random();
|
||||||
const data = await sendMessageOnTranslationTestRoom(`${newMessage[languages.default]} - ${randomMatcher}`);
|
const data = await sendMessageOnTranslationTestRoom(`${newMessage[languages.default]} - ${randomMatcher}`);
|
||||||
|
await searchMessage(`${newMessage[languages.default]} - ${randomMatcher}`, textMatcher); // will scroll the messages list to the last one
|
||||||
await waitForVisibleTextMatcher(`${newMessage[languages.translated]} - ${randomMatcher}`, textMatcher);
|
await waitForVisibleTextMatcher(`${newMessage[languages.translated]} - ${randomMatcher}`, textMatcher);
|
||||||
await deleteMessageOnTranslationTestRoom(data);
|
await deleteMessageOnTranslationTestRoom(data);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1760,7 +1760,7 @@
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 4.40.0;
|
MARKETING_VERSION = 4.41.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||||
|
@ -1799,7 +1799,7 @@
|
||||||
INFOPLIST_FILE = NotificationService/Info.plist;
|
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 4.40.0;
|
MARKETING_VERSION = 4.41.0;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.40.0</string>
|
<string>4.41.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.40.0</string>
|
<string>4.41.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>KeychainGroup</key>
|
<key>KeychainGroup</key>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "rocket-chat-reactnative",
|
"name": "rocket-chat-reactnative",
|
||||||
"version": "4.40.0",
|
"version": "4.41.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-native start",
|
"start": "react-native start",
|
||||||
|
@ -128,11 +128,12 @@
|
||||||
"react-native-safe-area-context": "3.2.0",
|
"react-native-safe-area-context": "3.2.0",
|
||||||
"react-native-screens": "3.13.1",
|
"react-native-screens": "3.13.1",
|
||||||
"react-native-scrollable-tab-view": "ptomasroos/react-native-scrollable-tab-view",
|
"react-native-scrollable-tab-view": "ptomasroos/react-native-scrollable-tab-view",
|
||||||
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.1",
|
"react-native-simple-crypto": "RocketChat/react-native-simple-crypto#0.5.2",
|
||||||
"react-native-skeleton-placeholder": "^5.2.3",
|
"react-native-skeleton-placeholder": "^5.2.3",
|
||||||
"react-native-slowlog": "^1.0.2",
|
"react-native-slowlog": "^1.0.2",
|
||||||
"react-native-svg": "^13.8.0",
|
"react-native-svg": "^13.8.0",
|
||||||
"react-native-ui-lib": "RocketChat/react-native-ui-lib#ef50151b8d9c1627ef527c620a1472868f9f4df8",
|
"react-native-ui-lib": "RocketChat/react-native-ui-lib#ef50151b8d9c1627ef527c620a1472868f9f4df8",
|
||||||
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-vector-icons": "9.1.0",
|
"react-native-vector-icons": "9.1.0",
|
||||||
"react-native-webview": "11.26.1",
|
"react-native-webview": "11.26.1",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^8.0.5",
|
||||||
|
|
59
yarn.lock
59
yarn.lock
|
@ -3139,6 +3139,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.21.0":
|
||||||
|
version "7.22.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4"
|
||||||
|
integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||||
version "7.9.6"
|
version "7.9.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
|
||||||
|
@ -8330,7 +8337,7 @@ buffer@^5.2.0:
|
||||||
base64-js "^1.0.2"
|
base64-js "^1.0.2"
|
||||||
ieee754 "^1.1.4"
|
ieee754 "^1.1.4"
|
||||||
|
|
||||||
buffer@^5.5.0:
|
buffer@^5.4.3, buffer@^5.5.0:
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||||
|
@ -9594,9 +9601,11 @@ damerau-levenshtein@^1.0.7:
|
||||||
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
||||||
|
|
||||||
date-fns@^2.29.3:
|
date-fns@^2.29.3:
|
||||||
version "2.29.3"
|
version "2.30.0"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
|
||||||
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
|
||||||
dayjs@^1.8.15:
|
dayjs@^1.8.15:
|
||||||
version "1.11.7"
|
version "1.11.7"
|
||||||
|
@ -17044,6 +17053,11 @@ punycode@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
|
punycode@^2.1.1:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
|
||||||
|
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||||
|
|
||||||
qr-image@^3.1.0:
|
qr-image@^3.1.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/qr-image/-/qr-image-3.2.0.tgz#9fa8295beae50c4a149cf9f909a1db464a8672e8"
|
resolved "https://registry.yarnpkg.com/qr-image/-/qr-image-3.2.0.tgz#9fa8295beae50c4a149cf9f909a1db464a8672e8"
|
||||||
|
@ -17614,9 +17628,9 @@ react-native-scrollable-tab-view@ptomasroos/react-native-scrollable-tab-view:
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
react-timer-mixin "^0.13.3"
|
react-timer-mixin "^0.13.3"
|
||||||
|
|
||||||
react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.1:
|
react-native-simple-crypto@RocketChat/react-native-simple-crypto#0.5.2:
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/dcf6eef5359c739d521371918e13a73f2ea6cb42"
|
resolved "https://codeload.github.com/RocketChat/react-native-simple-crypto/tar.gz/227e1a106e0507551a49df86c8d61ddbaf1e285c"
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js "^1.3.0"
|
base64-js "^1.3.0"
|
||||||
hex-lite "^1.5.0"
|
hex-lite "^1.5.0"
|
||||||
|
@ -17669,6 +17683,13 @@ react-native-ui-lib@RocketChat/react-native-ui-lib#ef50151b8d9c1627ef527c620a147
|
||||||
tinycolor2 "^1.4.2"
|
tinycolor2 "^1.4.2"
|
||||||
url-parse "^1.2.0"
|
url-parse "^1.2.0"
|
||||||
|
|
||||||
|
react-native-url-polyfill@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-2.0.0.tgz#db714520a2985cff1d50ab2e66279b9f91ffd589"
|
||||||
|
integrity sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA==
|
||||||
|
dependencies:
|
||||||
|
whatwg-url-without-unicode "8.0.0-3"
|
||||||
|
|
||||||
react-native-vector-icons@9.1.0:
|
react-native-vector-icons@9.1.0:
|
||||||
version "9.1.0"
|
version "9.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-9.1.0.tgz#52eaa205f5954d567b7048eb93d58ac854a2c59e"
|
resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-9.1.0.tgz#52eaa205f5954d567b7048eb93d58ac854a2c59e"
|
||||||
|
@ -18045,6 +18066,11 @@ regenerator-runtime@^0.13.7:
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
|
||||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||||
|
|
||||||
|
regenerator-runtime@^0.14.0:
|
||||||
|
version "0.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||||
|
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
|
||||||
|
|
||||||
regenerator-transform@^0.14.2:
|
regenerator-transform@^0.14.2:
|
||||||
version "0.14.4"
|
version "0.14.4"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
|
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
|
||||||
|
@ -18584,7 +18610,7 @@ semver-compare@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||||
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
|
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.2.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
|
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
|
@ -18599,6 +18625,11 @@ semver@7.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||||
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
|
||||||
|
|
||||||
|
semver@^5.2.0:
|
||||||
|
version "5.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
||||||
|
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
||||||
|
|
||||||
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||||
version "6.3.0"
|
version "6.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||||
|
@ -20817,6 +20848,11 @@ webidl-conversions@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||||
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
||||||
|
|
||||||
|
webidl-conversions@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
|
||||||
|
integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==
|
||||||
|
|
||||||
webpack-dev-middleware@^3.7.3:
|
webpack-dev-middleware@^3.7.3:
|
||||||
version "3.7.3"
|
version "3.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
||||||
|
@ -20905,6 +20941,15 @@ whatwg-fetch@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
|
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
|
||||||
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
|
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
|
||||||
|
|
||||||
|
whatwg-url-without-unicode@8.0.0-3:
|
||||||
|
version "8.0.0-3"
|
||||||
|
resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b"
|
||||||
|
integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==
|
||||||
|
dependencies:
|
||||||
|
buffer "^5.4.3"
|
||||||
|
punycode "^2.1.1"
|
||||||
|
webidl-conversions "^5.0.0"
|
||||||
|
|
||||||
whatwg-url@^5.0.0:
|
whatwg-url@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||||
|
|
Loading…
Reference in New Issue