Merge branch 'develop' into feat.new-audio-player
This commit is contained in:
commit
9854f1626d
|
@ -7,7 +7,7 @@ orbs:
|
|||
macos: &macos
|
||||
macos:
|
||||
xcode: "14.2.0"
|
||||
resource_class: macos.m1.large.gen1
|
||||
resource_class: macos.m1.medium.gen1
|
||||
|
||||
bash-env: &bash-env
|
||||
BASH_ENV: "~/.nvm/nvm.sh"
|
||||
|
@ -453,7 +453,7 @@ jobs:
|
|||
<<: *defaults
|
||||
executor:
|
||||
name: android/android-machine
|
||||
resource-class: xlarge
|
||||
resource-class: large
|
||||
tag: 2022.12.1
|
||||
environment:
|
||||
<<: *android-env
|
||||
|
@ -500,7 +500,7 @@ jobs:
|
|||
<<: *defaults
|
||||
executor:
|
||||
name: android/android-machine
|
||||
resource-class: xlarge
|
||||
resource-class: large
|
||||
tag: 2022.12.1
|
||||
parallelism: 4
|
||||
steps:
|
||||
|
|
|
@ -33,8 +33,10 @@ const getStories = () => {
|
|||
require("../app/containers/ReactionsList/ReactionsList.stories.tsx"),
|
||||
require("../app/containers/RoomHeader/RoomHeader.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/ServerItem/ServerItem.stories.tsx"),
|
||||
require("../app/containers/Status/Status.stories.tsx"),
|
||||
require("../app/containers/TextInput/TextInput.stories.tsx"),
|
||||
require("../app/containers/UIKit/UiKitMessage.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 - 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 - 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
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "4.40.0"
|
||||
versionName "4.41.0"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
if (!isFoss) {
|
||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import { useBackHandler } from '@react-native-community/hooks';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState, useCallback } from 'react';
|
||||
import { Keyboard } from 'react-native';
|
||||
import { Easing } from 'react-native-reanimated';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Keyboard, useWindowDimensions } from 'react-native';
|
||||
import { Easing, useDerivedValue, useSharedValue } from 'react-native-reanimated';
|
||||
import BottomSheet, { BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
||||
|
||||
import { useDimensions, useOrientation } from '../../dimensions';
|
||||
import { useTheme } from '../../theme';
|
||||
import { isIOS, isTablet } from '../../lib/methods/helpers';
|
||||
import { Handle } from './Handle';
|
||||
import { TActionSheetOptions } from './Provider';
|
||||
import BottomSheetContent from './BottomSheetContent';
|
||||
import styles, { ITEM_HEIGHT } from './styles';
|
||||
|
||||
const HANDLE_HEIGHT = isIOS ? 40 : 56;
|
||||
const MIN_SNAP_HEIGHT = 16;
|
||||
const CANCEL_HEIGHT = 64;
|
||||
import styles from './styles';
|
||||
|
||||
export const ACTION_SHEET_ANIMATION_DURATION = 250;
|
||||
|
||||
|
@ -32,33 +26,34 @@ const ActionSheet = React.memo(
|
|||
const bottomSheetRef = useRef<BottomSheet>(null);
|
||||
const [data, setData] = useState<TActionSheetOptions>({} as TActionSheetOptions);
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
const { height } = useDimensions();
|
||||
const { isLandscape } = useOrientation();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { width, height } = useWindowDimensions();
|
||||
const isLandscape = width > height;
|
||||
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(
|
||||
// Items height
|
||||
ITEM_HEIGHT * (data?.options?.length || 0) +
|
||||
// Handle height
|
||||
HANDLE_HEIGHT +
|
||||
// Custom header height
|
||||
(data?.headerHeight || 0) +
|
||||
// Insets bottom height (Notch devices)
|
||||
insets.bottom +
|
||||
// Cancel button height
|
||||
(data?.hasCancel ? CANCEL_HEIGHT : 0),
|
||||
height - MIN_SNAP_HEIGHT
|
||||
const handleContentLayout = useCallback(
|
||||
({
|
||||
nativeEvent: {
|
||||
layout: { height }
|
||||
}
|
||||
}) => {
|
||||
animatedContentHeight.value = height;
|
||||
},
|
||||
[animatedContentHeight]
|
||||
);
|
||||
|
||||
/*
|
||||
* 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 hide = () => {
|
||||
|
@ -67,6 +62,9 @@ const ActionSheet = React.memo(
|
|||
|
||||
const show = (options: TActionSheetOptions) => {
|
||||
setData(options);
|
||||
if (options.snaps?.length) {
|
||||
animatedDataSnaps.value = options.snaps;
|
||||
}
|
||||
toggleVisible();
|
||||
};
|
||||
|
||||
|
@ -104,6 +102,7 @@ const ActionSheet = React.memo(
|
|||
const onClose = () => {
|
||||
toggleVisible();
|
||||
data?.onClose && data?.onClose();
|
||||
animatedDataSnaps.value = [];
|
||||
};
|
||||
|
||||
const renderBackdrop = useCallback(
|
||||
|
@ -131,7 +130,10 @@ const ActionSheet = React.memo(
|
|||
{isVisible && (
|
||||
<BottomSheet
|
||||
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}
|
||||
animateOnMount={true}
|
||||
backdropComponent={renderBackdrop}
|
||||
|
@ -144,7 +146,13 @@ const ActionSheet = React.memo(
|
|||
enableContentPanningGesture={data?.enableContentPanningGesture ?? true}
|
||||
{...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>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Text } from 'react-native';
|
||||
import { Text, ViewProps } from 'react-native';
|
||||
import React from 'react';
|
||||
import { BottomSheetView, BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import { useTheme } from '../../theme';
|
||||
|
@ -15,10 +16,12 @@ interface IBottomSheetContentProps {
|
|||
options?: TActionSheetOptionsItem[];
|
||||
hide: () => void;
|
||||
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 { bottom } = useSafeAreaInsets();
|
||||
|
||||
const renderFooter = () =>
|
||||
hasCancel ? (
|
||||
|
@ -42,18 +45,19 @@ const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: I
|
|||
keyExtractor={item => item.title}
|
||||
bounces={true}
|
||||
renderItem={renderItem}
|
||||
style={{ backgroundColor: colors.focusedBackground }}
|
||||
style={{ backgroundColor: colors.focusedBackground, paddingBottom: bottom }}
|
||||
keyboardDismissMode='interactive'
|
||||
indicatorStyle='black'
|
||||
contentContainerStyle={styles.content}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
ListHeaderComponent={List.Separator}
|
||||
ListFooterComponent={renderFooter}
|
||||
onLayout={onLayout}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BottomSheetView testID='action-sheet' style={styles.contentContainer}>
|
||||
<BottomSheetView testID='action-sheet' style={[styles.contentContainer, { paddingBottom: bottom }]} onLayout={onLayout}>
|
||||
{children}
|
||||
</BottomSheetView>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,6 @@ export type TActionSheetOptionsItem = {
|
|||
|
||||
export type TActionSheetOptions = {
|
||||
options?: TActionSheetOptionsItem[];
|
||||
headerHeight?: number;
|
||||
customHeader?: React.ReactElement | null;
|
||||
hasCancel?: boolean;
|
||||
type?: string;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
@ -37,21 +37,16 @@ const IncomingCallHeader = React.memo(
|
|||
const [mic, setMic] = useState(true);
|
||||
const [cam, setCam] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||
const styles = useStyle();
|
||||
|
||||
const insets = useSafeAreaInsets();
|
||||
const { height, width } = useWindowDimensions();
|
||||
const isLandscape = width > height;
|
||||
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
(isMasterDetail || isLandscape) && styles.small,
|
||||
isMasterDetail && styles.small,
|
||||
{
|
||||
marginTop: insets.top
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import { themes } from '../../lib/constants';
|
|||
import { useTheme } from '../../theme';
|
||||
import { ROW_HEIGHT } from '../RoomItem';
|
||||
import { goRoom } from '../../lib/methods/helpers/goRoom';
|
||||
import { useOrientation } from '../../dimensions';
|
||||
import { IApplicationState, ISubscription, SubscriptionType } from '../../definitions';
|
||||
import { hideNotification } from '../../lib/methods/helpers/notifications';
|
||||
|
||||
|
@ -76,8 +75,6 @@ const styles = StyleSheet.create({
|
|||
const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifierComponent) => {
|
||||
const { theme } = useTheme();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { isLandscape } = useOrientation();
|
||||
|
||||
const { text, payload } = notification;
|
||||
const { type, rid } = payload;
|
||||
const name = type === 'd' ? payload.sender.username : payload.name;
|
||||
|
@ -104,7 +101,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
|
|||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
(isMasterDetail || isLandscape) && styles.small,
|
||||
isMasterDetail && styles.small,
|
||||
{
|
||||
backgroundColor: themes[theme].focusedBackground,
|
||||
borderColor: themes[theme].separatorColor,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 { themes } from '../../lib/constants';
|
||||
|
@ -8,7 +8,6 @@ import shortnameToUnicode from '../../lib/methods/helpers/shortnameToUnicode';
|
|||
import { addFrequentlyUsed } from '../../lib/methods';
|
||||
import { useFrequentlyUsedEmoji } from '../../lib/hooks';
|
||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||
import { useDimensions } from '../../dimensions';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import { IEmoji, TAnyMessageModel } from '../../definitions';
|
||||
import Touch from '../Touch';
|
||||
|
@ -32,7 +31,6 @@ interface THeaderFooter {
|
|||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
export const HEADER_HEIGHT = 36;
|
||||
const ITEM_SIZE = 36;
|
||||
const CONTAINER_MARGIN = 8;
|
||||
const ITEM_MARGIN = 8;
|
||||
|
@ -86,11 +84,10 @@ const HeaderFooter = ({ onReaction, theme }: THeaderFooter) => (
|
|||
);
|
||||
|
||||
const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader) => {
|
||||
const { width, height } = useDimensions();
|
||||
const { width } = useWindowDimensions();
|
||||
const { theme } = useTheme();
|
||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji(true);
|
||||
const isLandscape = width > height;
|
||||
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||
const size = (isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||
const quantity = Math.trunc(size / (ITEM_SIZE + ITEM_MARGIN * 2) - 1);
|
||||
|
||||
const onReaction: TOnReaction = ({ emoji }) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { LISTENER } from '../Toast';
|
|||
import EventEmitter from '../../lib/methods/helpers/events';
|
||||
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
|
||||
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 { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||
import { getPermalinkMessage } from '../../lib/methods';
|
||||
|
@ -511,7 +511,6 @@ const MessageActions = React.memo(
|
|||
await getPermissions();
|
||||
showActionSheet({
|
||||
options: getOptions(message),
|
||||
headerHeight: HEADER_HEIGHT,
|
||||
customHeader:
|
||||
!isReadOnly || room.reactWhenReadOnly ? (
|
||||
<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 range from 'lodash/range';
|
||||
import { View } from 'react-native';
|
||||
import * as Animatable from 'react-native-animatable';
|
||||
import * as Haptics from 'expo-haptics';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
|
||||
import styles from './styles';
|
||||
import Button from './Button';
|
||||
|
@ -16,7 +15,6 @@ import LockIcon from './LockIcon';
|
|||
import Title from './Title';
|
||||
import Subtitle from './Subtitle';
|
||||
import { useDimensions } from '../../../dimensions';
|
||||
import { isTablet } from '../../../lib/methods/helpers';
|
||||
|
||||
interface IPasscodeBase {
|
||||
type: string;
|
||||
|
@ -37,18 +35,6 @@ export interface IBase {
|
|||
|
||||
const Base = forwardRef<IBase, IPasscodeBase>(
|
||||
({ type, onEndProcess, previousPasscode, title, subtitle, onError, showBiometry, onBiometryPress }, ref) => {
|
||||
useLayoutEffect(() => {
|
||||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!isTablet) {
|
||||
Orientation.unlockAllOrientations();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { theme } = useTheme();
|
||||
const { height } = useDimensions();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { IReaction } from '../../definitions';
|
|||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import I18n from '../../i18n';
|
||||
import styles, { MIN_TAB_WIDTH } from './styles';
|
||||
import { useDimensions, useOrientation } from '../../dimensions';
|
||||
import { useDimensions } from '../../dimensions';
|
||||
|
||||
interface ITabBarItem {
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
|
@ -55,10 +55,8 @@ const TabBarItem = ({ tab, index, goToPage, getCustomEmoji }: ITabBarItem) => {
|
|||
};
|
||||
|
||||
const ReactionsTabBar = ({ tabs, activeTab, goToPage, getCustomEmoji }: IReactionsTabBar): React.ReactElement => {
|
||||
const { isLandscape } = useOrientation();
|
||||
const { width } = useDimensions();
|
||||
const reactionsListWidth = isLandscape ? width / 2 : width;
|
||||
const tabWidth = tabs && Math.max(reactionsListWidth / tabs.length, MIN_TAB_WIDTH);
|
||||
const tabWidth = tabs && Math.max(width / tabs.length, MIN_TAB_WIDTH);
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<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 = () => (
|
||||
<>
|
||||
<HeaderExample title={() => <RoomHeader tmid='123' parentTitle='parent title' />} />
|
||||
|
|
|
@ -63,10 +63,11 @@ interface IRoomHeader {
|
|||
type: string;
|
||||
width: number;
|
||||
height: number;
|
||||
roomUserId?: string | null;
|
||||
prid?: string;
|
||||
tmid?: string;
|
||||
teamMain?: boolean;
|
||||
status: TUserStatus;
|
||||
status?: TUserStatus;
|
||||
usersTyping: [];
|
||||
isGroupChat?: boolean;
|
||||
parentTitle?: string;
|
||||
|
@ -130,6 +131,7 @@ const Header = React.memo(
|
|||
status,
|
||||
width,
|
||||
height,
|
||||
roomUserId,
|
||||
prid,
|
||||
tmid,
|
||||
onPress,
|
||||
|
@ -154,7 +156,13 @@ const Header = React.memo(
|
|||
if (tmid) {
|
||||
renderFunc = () => (
|
||||
<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}>
|
||||
{parentTitle}
|
||||
</Text>
|
||||
|
@ -176,6 +184,7 @@ const Header = React.memo(
|
|||
<View style={styles.titleContainer}>
|
||||
{tmid ? null : (
|
||||
<RoomTypeIcon
|
||||
userId={roomUserId}
|
||||
type={prid ? 'discussion' : type}
|
||||
isGroupChat={isGroupChat}
|
||||
status={status}
|
||||
|
|
|
@ -39,14 +39,13 @@ const RoomHeaderContainer = React.memo(
|
|||
visitor
|
||||
}: IRoomHeaderContainerProps) => {
|
||||
let subtitle: string | undefined;
|
||||
let status: TUserStatus = 'offline';
|
||||
let statusVisitor: TUserStatus | undefined;
|
||||
let statusText: string | undefined;
|
||||
const { width, height } = useDimensions();
|
||||
|
||||
const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
|
||||
const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
|
||||
const connected = useSelector((state: IApplicationState) => state.meteor.connected);
|
||||
const presenceDisabled = useSelector((state: IApplicationState) => state.settings.Presence_broadcast_disabled);
|
||||
const activeUser = useSelector(
|
||||
(state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
|
||||
shallowEqual
|
||||
|
@ -62,28 +61,23 @@ const RoomHeaderContainer = React.memo(
|
|||
|
||||
if (connected) {
|
||||
if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
|
||||
if (presenceDisabled) {
|
||||
status = 'disabled';
|
||||
} else {
|
||||
const { status: statusActiveUser, statusText: statusTextActiveUser } = activeUser;
|
||||
status = statusActiveUser;
|
||||
statusText = statusTextActiveUser;
|
||||
}
|
||||
const { statusText: statusTextActiveUser } = activeUser;
|
||||
statusText = statusTextActiveUser;
|
||||
} else if (type === 'l' && visitor?.status) {
|
||||
const { status: statusVisitor } = visitor;
|
||||
status = statusVisitor;
|
||||
({ status: statusVisitor } = visitor);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<RoomHeader
|
||||
roomUserId={roomUserId}
|
||||
prid={prid}
|
||||
tmid={tmid}
|
||||
title={title}
|
||||
subtitle={type === 'd' ? statusText : subtitle}
|
||||
type={type}
|
||||
teamMain={teamMain}
|
||||
status={status}
|
||||
status={statusVisitor}
|
||||
width={width}
|
||||
height={height}
|
||||
usersTyping={usersTyping}
|
||||
|
|
|
@ -12,6 +12,7 @@ const IconOrAvatar = ({
|
|||
type,
|
||||
rid,
|
||||
showAvatar,
|
||||
userId,
|
||||
prid,
|
||||
status,
|
||||
isGroupChat,
|
||||
|
@ -30,6 +31,7 @@ const IconOrAvatar = ({
|
|||
return (
|
||||
<View style={styles.typeIcon}>
|
||||
<TypeIcon
|
||||
userId={userId}
|
||||
type={type}
|
||||
prid={prid}
|
||||
status={status}
|
||||
|
|
|
@ -41,14 +41,14 @@ export const Touch = () => <RoomItem onPress={() => alert('on press')} onLongPre
|
|||
|
||||
export const User = () => (
|
||||
<>
|
||||
<RoomItem name='diego.mello' avatar='diego.mello' />
|
||||
<RoomItem name={longText} />
|
||||
<RoomItem name='diego.mello' avatar='diego.mello' userId='abc' />
|
||||
<RoomItem name={longText} userId='abc' />
|
||||
</>
|
||||
);
|
||||
|
||||
export const Type = () => (
|
||||
<>
|
||||
<RoomItem type='d' />
|
||||
<RoomItem type='d' userId='abc' />
|
||||
<RoomItem type='c' />
|
||||
<RoomItem type='p' />
|
||||
<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 = () => (
|
||||
<>
|
||||
<RoomItem alert />
|
||||
|
@ -159,7 +147,6 @@ export const ExpandedRoomItemWithoutAvatar = () => (
|
|||
showAvatar={false}
|
||||
/>
|
||||
<RoomItem
|
||||
status='online'
|
||||
showLastMessage
|
||||
alert
|
||||
tunread={[1]}
|
||||
|
@ -167,14 +154,7 @@ export const ExpandedRoomItemWithoutAvatar = () => (
|
|||
displayMode={DisplayMode.Expanded}
|
||||
showAvatar={false}
|
||||
/>
|
||||
<RoomItem
|
||||
status='online'
|
||||
showLastMessage
|
||||
alert
|
||||
lastMessage={lastMessage}
|
||||
displayMode={DisplayMode.Expanded}
|
||||
showAvatar={false}
|
||||
/>
|
||||
<RoomItem showLastMessage alert lastMessage={lastMessage} displayMode={DisplayMode.Expanded} showAvatar={false} />
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import { IRoomItemProps } from './interfaces';
|
|||
|
||||
const RoomItem = ({
|
||||
rid,
|
||||
userId,
|
||||
type,
|
||||
prid,
|
||||
name,
|
||||
|
@ -74,6 +75,7 @@ const RoomItem = ({
|
|||
accessibilityLabel={accessibilityLabel}
|
||||
avatar={avatar}
|
||||
type={type}
|
||||
userId={userId}
|
||||
rid={rid}
|
||||
prid={prid}
|
||||
status={status}
|
||||
|
@ -89,6 +91,7 @@ const RoomItem = ({
|
|||
<View style={styles.titleContainer}>
|
||||
{showAvatar ? (
|
||||
<TypeIcon
|
||||
userId={userId}
|
||||
type={type}
|
||||
prid={prid}
|
||||
status={status}
|
||||
|
@ -125,6 +128,7 @@ const RoomItem = ({
|
|||
) : (
|
||||
<View style={[styles.titleContainer, styles.flex]}>
|
||||
<TypeIcon
|
||||
userId={userId}
|
||||
type={type}
|
||||
prid={prid}
|
||||
status={status}
|
||||
|
|
|
@ -3,8 +3,9 @@ import React from 'react';
|
|||
import RoomTypeIcon from '../RoomTypeIcon';
|
||||
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
|
||||
userId={userId}
|
||||
type={prid ? 'discussion' : type}
|
||||
isGroupChat={isGroupChat}
|
||||
status={status}
|
||||
|
|
|
@ -2,13 +2,11 @@ import React, { useEffect, useReducer, useRef } from 'react';
|
|||
import { Subscription } from 'rxjs';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import { getUserPresence } from '../../lib/methods';
|
||||
import { isGroupChat } from '../../lib/methods/helpers';
|
||||
import { formatDate } from '../../lib/methods/helpers/room';
|
||||
import { IRoomItemContainerProps } from './interfaces';
|
||||
import RoomItem from './RoomItem';
|
||||
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
||||
import { useUserStatus } from './useUserStatus';
|
||||
|
||||
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
||||
|
||||
|
@ -44,8 +42,7 @@ const RoomItemContainer = React.memo(
|
|||
const alert = item.alert || item.tunread?.length;
|
||||
const [_, forceUpdate] = useReducer(x => x + 1, 1);
|
||||
const roomSubscription = useRef<Subscription | null>(null);
|
||||
|
||||
const { connected, status } = useUserStatus(item.t, item?.visitor?.status, id);
|
||||
const userId = item.t === 'd' && id && !isGroupChat(item) ? id : null;
|
||||
|
||||
useEffect(() => {
|
||||
const init = () => {
|
||||
|
@ -61,13 +58,6 @@ const RoomItemContainer = React.memo(
|
|||
return () => roomSubscription.current?.unsubscribe();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const isDirect = !!(item.t === 'd' && id && !isGroupChat(item));
|
||||
if (connected && isDirect) {
|
||||
getUserPresence(id);
|
||||
}
|
||||
}, [connected]);
|
||||
|
||||
const handleOnPress = () => onPress(item);
|
||||
|
||||
const handleOnLongPress = () => onLongPress && onLongPress(item);
|
||||
|
@ -98,6 +88,7 @@ const RoomItemContainer = React.memo(
|
|||
width={width}
|
||||
favorite={item.f}
|
||||
rid={item.rid}
|
||||
userId={userId}
|
||||
toggleFav={toggleFav}
|
||||
toggleRead={toggleRead}
|
||||
hideChannel={hideChannel}
|
||||
|
@ -105,7 +96,6 @@ const RoomItemContainer = React.memo(
|
|||
type={item.t}
|
||||
isFocused={isFocused}
|
||||
prid={item.prid}
|
||||
status={status}
|
||||
hideUnreadStatus={item.hideUnreadStatus}
|
||||
hideMentionStatus={item.hideMentionStatus}
|
||||
alert={alert}
|
||||
|
@ -124,7 +114,8 @@ const RoomItemContainer = React.memo(
|
|||
autoJoin={autoJoin}
|
||||
showAvatar={showAvatar}
|
||||
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;
|
||||
avatar: string;
|
||||
type: string;
|
||||
userId: string | null;
|
||||
rid: string;
|
||||
children: React.ReactElement;
|
||||
displayMode: string;
|
||||
|
@ -50,6 +51,7 @@ export interface IWrapperProps {
|
|||
}
|
||||
|
||||
export interface ITypeIconProps {
|
||||
userId: string | null;
|
||||
type: string;
|
||||
status: TUserStatus;
|
||||
prid: string;
|
||||
|
@ -144,6 +146,7 @@ export interface IIconOrAvatar {
|
|||
avatar: string;
|
||||
type: string;
|
||||
rid: string;
|
||||
userId: string | null;
|
||||
showAvatar: boolean;
|
||||
displayMode: 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 { CustomIcon, TIconsName } from '../CustomIcon';
|
||||
import { STATUS_COLORS, themes } from '../../lib/constants';
|
||||
import Status from '../Status/Status';
|
||||
import { themes } from '../../lib/constants';
|
||||
import Status from '../Status';
|
||||
import { useTheme } from '../../theme';
|
||||
import { TUserStatus, IOmnichannelSource } from '../../definitions';
|
||||
|
||||
|
@ -15,6 +15,7 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
interface IRoomTypeIcon {
|
||||
userId?: string | null;
|
||||
type: string;
|
||||
isGroupChat?: boolean;
|
||||
teamMain?: boolean;
|
||||
|
@ -24,44 +25,38 @@ interface IRoomTypeIcon {
|
|||
sourceType?: IOmnichannelSource;
|
||||
}
|
||||
|
||||
const RoomTypeIcon = React.memo(({ type, isGroupChat, status, style, teamMain, size = 16, sourceType }: IRoomTypeIcon) => {
|
||||
const { theme } = useTheme();
|
||||
const RoomTypeIcon = React.memo(
|
||||
({ userId, type, isGroupChat, status, style, teamMain, size = 16, sourceType }: IRoomTypeIcon) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const color = themes[theme].titleText;
|
||||
const iconStyle = [styles.icon, { color }, style];
|
||||
|
||||
if (type === 'd' && !isGroupChat) {
|
||||
if (!status) {
|
||||
status = 'offline';
|
||||
if (!type) {
|
||||
return null;
|
||||
}
|
||||
return <Status style={[iconStyle, { color: STATUS_COLORS[status] }]} size={size} status={status} />;
|
||||
}
|
||||
|
||||
if (type === 'l') {
|
||||
return <OmnichannelRoomIcon style={[styles.icon, style]} size={size} type={type} status={status} sourceType={sourceType} />;
|
||||
}
|
||||
const iconStyle = [styles.icon, style];
|
||||
|
||||
// TODO: move this to a separate function
|
||||
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') {
|
||||
if (isGroupChat) {
|
||||
if (type === 'd' && !isGroupChat && userId) {
|
||||
return <Status id={userId} style={iconStyle} size={size} status={status} />;
|
||||
}
|
||||
|
||||
if (type === 'l') {
|
||||
return <OmnichannelRoomIcon style={iconStyle} size={size} type={type} status={status} sourceType={sourceType} />;
|
||||
}
|
||||
|
||||
// TODO: move this to a separate function
|
||||
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';
|
||||
} 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;
|
||||
|
|
|
@ -6,8 +6,6 @@ import { useTheme } from '../theme';
|
|||
import sharedStyles from '../views/Styles';
|
||||
import { themes } from '../lib/constants';
|
||||
import { TextInput } from './TextInput';
|
||||
import { isIOS, isTablet } from '../lib/methods/helpers';
|
||||
import { useOrientation } from '../dimensions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -16,7 +14,8 @@ const styles = StyleSheet.create({
|
|||
marginLeft: 0
|
||||
},
|
||||
title: {
|
||||
...sharedStyles.textSemibold
|
||||
...sharedStyles.textSemibold,
|
||||
fontSize: 16
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -28,15 +27,12 @@ interface ISearchHeaderProps {
|
|||
const SearchHeader = ({ onSearchChangeText, testID }: ISearchHeaderProps): JSX.Element => {
|
||||
const { theme } = useTheme();
|
||||
const isLight = theme === 'light';
|
||||
const { isLandscape } = useOrientation();
|
||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
||||
const titleFontSize = 16 * scale;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
style={[styles.title, isLight && { color: themes[theme].headerTitleColor }, { fontSize: titleFontSize }]}
|
||||
style={[styles.title, isLight && { color: themes[theme].headerTitleColor }]}
|
||||
placeholder={I18n.t('Search')}
|
||||
onChangeText={onSearchChangeText}
|
||||
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 { 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 isNameValid = IconSet.hasIcon(name);
|
||||
const iconName = isNameValid ? name : 'status-offline';
|
||||
|
|
|
@ -5,5 +5,10 @@ import { TUserStatus } from '../../definitions';
|
|||
export interface IStatus extends TextProps {
|
||||
id: string;
|
||||
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 { IStatus } from './definition';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import { getUserPresence } from '../../lib/methods';
|
||||
|
||||
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
||||
const status = useAppSelector(state => {
|
||||
const StatusContainer = ({ id, style, status, size = 32, ...props }: IStatus): React.ReactElement => {
|
||||
const connected = useAppSelector(state => state.meteor.connected);
|
||||
const statusState = useAppSelector(state => {
|
||||
if (state.settings.Presence_broadcast_disabled) {
|
||||
return 'disabled';
|
||||
}
|
||||
if (state.meteor.connected) {
|
||||
return state.activeUsers[id] && state.activeUsers[id].status;
|
||||
if (state.meteor.connected && state.activeUsers[id]) {
|
||||
return state.activeUsers[id].status;
|
||||
}
|
||||
if (!state.meteor.connected) {
|
||||
return 'offline';
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -69,7 +69,11 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IT
|
|||
<View style={styles.detailsContainer}>
|
||||
<View style={styles.detailContainer}>
|
||||
<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}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
@ -87,8 +87,7 @@ export const MultiSelect = React.memo(
|
|||
selectedItems={selected}
|
||||
/>
|
||||
),
|
||||
onClose,
|
||||
headerHeight: 275
|
||||
onClose
|
||||
});
|
||||
};
|
||||
const onHide = () => {
|
||||
|
|
|
@ -252,6 +252,47 @@ export const SectionMultiSelect = () =>
|
|||
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';
|
||||
|
|
|
@ -174,7 +174,12 @@ export const ModalFormInput = () =>
|
|||
text: 'Set a date',
|
||||
emoji: true
|
||||
}
|
||||
},
|
||||
}
|
||||
]);
|
||||
ModalFormInput.storyName = 'Modal - Form Input';
|
||||
|
||||
export const ModalMultiSelect = () =>
|
||||
UiKitModal([
|
||||
{
|
||||
type: 'input',
|
||||
element: {
|
||||
|
@ -200,10 +205,48 @@ export const ModalFormInput = () =>
|
|||
type: 'plain_text',
|
||||
text: 'Share with...',
|
||||
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 = () =>
|
||||
UiKitModal([
|
||||
|
|
|
@ -138,7 +138,7 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
|||
|
||||
multiStaticSelect(element: IElement, context: BlockContext) {
|
||||
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 />;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const Thread = React.memo(
|
|||
const { theme } = useTheme();
|
||||
const { threadBadgeColor, toggleFollowThread, user, replies } = useContext(MessageContext);
|
||||
|
||||
if (!tlm || isThreadRoom || tcount === 0) {
|
||||
if (!tlm || isThreadRoom || tcount === 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];
|
||||
|
|
|
@ -3,6 +3,6 @@ export type SubscriptionsEndpoints = {
|
|||
POST: (params: { firstUnreadMessage: { _id: string } } | { roomId: string }) => {};
|
||||
};
|
||||
'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",
|
||||
"Disabled_E2E_Encryption_for_this_room": "disabled E2E encryption for this room",
|
||||
"Teams": "Teams",
|
||||
"No_team_channels_found": "No channels found",
|
||||
"Team_not_found": "Team not found",
|
||||
"Create_Team": "Create team",
|
||||
"Team_Name": "Team name",
|
||||
|
@ -744,5 +743,6 @@
|
|||
"accept": "Accept",
|
||||
"Incoming_call_from": "Incoming call from",
|
||||
"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",
|
||||
"Nickname": "Apelido",
|
||||
"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 { Provider } from 'react-redux';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
|
||||
import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app';
|
||||
import { deepLinkingOpen } from './actions/deepLinking';
|
||||
|
@ -104,6 +105,9 @@ export default class Root extends React.Component<{}, IState> {
|
|||
};
|
||||
if (isTablet) {
|
||||
this.initTablet();
|
||||
Orientation.unlockAllOrientations();
|
||||
} else {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
setNativeTheme(theme);
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ export const colors = {
|
|||
headerTintColor: '#f9f9f9',
|
||||
headerTitleColor: '#f9f9f9',
|
||||
headerSecondaryText: '#9297a2',
|
||||
toastBackground: '#0C0D0F',
|
||||
toastBackground: '#54585e',
|
||||
videoBackground: '#1f2329',
|
||||
favoriteBackground: '#ffbb00',
|
||||
hideBackground: '#54585e',
|
||||
|
@ -228,7 +228,7 @@ export const colors = {
|
|||
headerTintColor: '#f9f9f9',
|
||||
headerTitleColor: '#f9f9f9',
|
||||
headerSecondaryText: '#b2b8c6',
|
||||
toastBackground: '#0C0D0F',
|
||||
toastBackground: '#54585e',
|
||||
videoBackground: '#1f2329',
|
||||
favoriteBackground: '#ffbb00',
|
||||
hideBackground: '#54585e',
|
||||
|
|
|
@ -102,6 +102,9 @@ export const defaultSettings = {
|
|||
E2E_Enable: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
E2E_Enabled_Default_PrivateRooms: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Accounts_Directory_DefaultView: {
|
||||
type: 'valueAsString'
|
||||
},
|
||||
|
|
|
@ -199,10 +199,16 @@ class Encryption {
|
|||
|
||||
// Encode the private key
|
||||
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}`);
|
||||
|
||||
if (!publicKey) {
|
||||
throw new Error('Public key not found in local storage, password not changed');
|
||||
}
|
||||
|
||||
// Send the new keys to the server
|
||||
await Services.e2eSetUserPublicAndPrivateKeys(EJSON.stringify(publicKey), encodedPrivateKey);
|
||||
await Services.e2eSetUserPublicAndPrivateKeys(publicKey, encodedPrivateKey);
|
||||
};
|
||||
|
||||
// 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 React, { useState } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { useAppSelector } from '..';
|
||||
|
@ -13,7 +12,6 @@ import Ringer, { ERingerSounds } from '../../../containers/Ringer';
|
|||
import i18n from '../../../i18n';
|
||||
import { getUserSelector } from '../../../selectors/login';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { isIOS } from '../../methods/helpers';
|
||||
import useUserData from '../useUserData';
|
||||
|
||||
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);
|
||||
|
||||
// fix safe area bottom padding on iOS
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = isIOS && insets.bottom ? 8 : 0;
|
||||
|
||||
React.useEffect(
|
||||
() => () => {
|
||||
if (calling) {
|
||||
|
@ -42,10 +36,7 @@ export default function StartACallActionSheet({ rid }: { rid: string }): React.R
|
|||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[style.actionSheetContainer, { paddingBottom }]}
|
||||
onLayout={e => setContainerWidth(e.nativeEvent.layout.width / 2)}
|
||||
>
|
||||
<View style={style.actionSheetContainer} onLayout={e => setContainerWidth(e.nativeEvent.layout.width / 2)}>
|
||||
{calling ? <Ringer ringer={ERingerSounds.DIALTONE} /> : null}
|
||||
<CallHeader
|
||||
title={calling && user.direct ? i18n.t('Calling') : i18n.t('Start_a_call')}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { Camera } from 'expo-camera';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { useActionSheet } from '../../../containers/ActionSheet';
|
||||
import i18n from '../../../i18n';
|
||||
import { getUserSelector } from '../../../selectors/login';
|
||||
import { compareServerVersion, showErrorAlert } from '../../methods/helpers';
|
||||
import log from '../../methods/helpers/log';
|
||||
import { handleAndroidBltPermission } from '../../methods/videoConf';
|
||||
import { Services } from '../../services';
|
||||
import { useAppSelector } from '../useAppSelector';
|
||||
import { useSnaps } from '../useSnaps';
|
||||
import StartACallActionSheet from './StartACallActionSheet';
|
||||
import { useVideoConfCall } from './useVideoConfCall';
|
||||
|
||||
|
@ -18,9 +18,9 @@ const availabilityErrors = {
|
|||
NO_APP: 'no-videoconf-provider-app'
|
||||
} as const;
|
||||
|
||||
const handleErrors = (isAdmin: boolean, error: typeof availabilityErrors[keyof typeof availabilityErrors]) => {
|
||||
if (isAdmin) return showErrorAlert(i18n.t(`admin-${error}-body`), i18n.t(`admin-${error}-header`));
|
||||
return showErrorAlert(i18n.t(`${error}-body`), i18n.t(`${error}-header`));
|
||||
const handleErrors = (isAdmin: boolean, error: keyof typeof availabilityErrors) => {
|
||||
const key = isAdmin ? `admin-${error}` : error;
|
||||
showErrorAlert(i18n.t(`${key}-body`), i18n.t(`${key}-header`));
|
||||
};
|
||||
|
||||
export const useVideoConf = (
|
||||
|
@ -32,48 +32,42 @@ export const useVideoConf = (
|
|||
const { callEnabled, disabledTooltip } = useVideoConfCall(rid);
|
||||
|
||||
const [permission, requestPermission] = Camera.useCameraPermissions();
|
||||
|
||||
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 () => {
|
||||
if (callEnabled) {
|
||||
if (isServer5OrNewer) {
|
||||
try {
|
||||
await Services.videoConferenceGetCapabilities();
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
const isAdmin = !!user.roles?.includes('admin');
|
||||
switch (error?.error) {
|
||||
case availabilityErrors.NOT_CONFIGURED:
|
||||
return handleErrors(isAdmin, availabilityErrors.NOT_CONFIGURED);
|
||||
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);
|
||||
}
|
||||
}
|
||||
const canInitAnCall = async (): Promise<boolean> => {
|
||||
if (!callEnabled) return false;
|
||||
|
||||
if (isServer5OrNewer) {
|
||||
try {
|
||||
await Services.videoConferenceGetCapabilities();
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
const isAdmin = !!user.roles?.includes('admin');
|
||||
handleErrors(isAdmin, error?.error || 'NOT_CONFIGURED');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const showInitCallActionSheet = async () => {
|
||||
const canInit = await canInitAnCall();
|
||||
if (canInit) {
|
||||
showActionSheet({
|
||||
children: <StartACallActionSheet rid={rid} />,
|
||||
snaps
|
||||
});
|
||||
if (!permission?.granted) {
|
||||
requestPermission();
|
||||
handleAndroidBltPermission();
|
||||
try {
|
||||
const canInit = await canInitAnCall();
|
||||
if (canInit) {
|
||||
showActionSheet({
|
||||
children: <StartACallActionSheet rid={rid} />,
|
||||
snaps: [480]
|
||||
});
|
||||
|
||||
if (!permission?.granted) {
|
||||
requestPermission();
|
||||
handleAndroidBltPermission();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ const serverInfoKeys = [
|
|||
'Force_Screen_Lock',
|
||||
'Force_Screen_Lock_After',
|
||||
'uniqueID',
|
||||
'E2E_Enable'
|
||||
'E2E_Enable',
|
||||
'E2E_Enabled_Default_PrivateRooms'
|
||||
];
|
||||
|
||||
// these settings are used only on onboarding process
|
||||
|
@ -85,6 +86,9 @@ const serverInfoUpdate = async (serverInfo: IPreparedSettings[], iconSetting: IS
|
|||
if (setting._id === 'E2E_Enable') {
|
||||
return { ...allSettings, E2E_Enable: setting.valueAsBoolean };
|
||||
}
|
||||
if (setting._id === 'E2E_Enabled_Default_PrivateRooms') {
|
||||
return { ...allSettings, E2E_Enabled_Default_PrivateRooms: setting.valueAsBoolean };
|
||||
}
|
||||
return allSettings;
|
||||
}, {});
|
||||
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
import { URL } from 'react-native-url-polyfill';
|
||||
|
||||
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 => {
|
||||
if (LOCAL_DOCUMENT_DIRECTORY && attachmentUrl?.startsWith(LOCAL_DOCUMENT_DIRECTORY)) {
|
||||
return attachmentUrl;
|
||||
|
@ -8,7 +17,7 @@ export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: s
|
|||
if (attachmentUrl.includes('rc_token')) {
|
||||
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 (/SUB/.test(key)) {
|
||||
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 room = batch[roomQueueId] as IRoom;
|
||||
delete batch[roomQueueId];
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
import {
|
||||
IAvatarSuggestion,
|
||||
IMessage,
|
||||
INotificationPreferences,
|
||||
IPreviewItem,
|
||||
IProfileParams,
|
||||
IRoom,
|
||||
IRoomNotifications,
|
||||
SubscriptionType,
|
||||
IServerRoom,
|
||||
IUser,
|
||||
IAvatarSuggestion,
|
||||
IProfileParams,
|
||||
RoomType,
|
||||
IServerRoom
|
||||
SubscriptionType
|
||||
} from '../../definitions';
|
||||
import { TParams } from '../../definitions/ILivechatEditView';
|
||||
import { ILivechatTag } from '../../definitions/ILivechatTag';
|
||||
import { ISpotlight } from '../../definitions/ISpotlight';
|
||||
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 { TParams } from '../../definitions/ILivechatEditView';
|
||||
import { store as reduxStore } from '../store/auxStore';
|
||||
import { getDeviceToken } from '../notifications';
|
||||
import { RoomTypes, roomTypeToApiType, unsubscribeRooms } from '../methods';
|
||||
import sdk from './sdk';
|
||||
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 = ({
|
||||
name,
|
||||
|
@ -310,11 +312,33 @@ export const setReaction = (emoji: string, messageId: string) =>
|
|||
// RC 0.62.2
|
||||
sdk.post('chat.react', { emoji, messageId });
|
||||
|
||||
export const toggleRead = (read: boolean, roomId: string) => {
|
||||
if (read) {
|
||||
return sdk.post('subscriptions.unread', { roomId });
|
||||
/**
|
||||
* Toggles the read status of a room.
|
||||
*
|
||||
* @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 = (
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import useDeepCompareEffect from 'use-deep-compare-effect';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import Modal from 'react-native-modal';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import { useTheme } from '../theme';
|
||||
import { hasNotch, isTablet } from '../lib/methods/helpers';
|
||||
import { hasNotch } from '../lib/methods/helpers';
|
||||
import { PasscodeChoose } from '../containers/Passcode';
|
||||
import EventEmitter from '../lib/methods/helpers/events';
|
||||
import { CustomIcon } from '../containers/CustomIcon';
|
||||
|
@ -65,14 +64,8 @@ const ChangePasscodeView = React.memo(() => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
const listener = EventEmitter.addEventListener(CHANGE_PASSCODE_EMITTER, showChangePasscode);
|
||||
return () => {
|
||||
if (!isTablet) {
|
||||
Orientation.unlockAllOrientations();
|
||||
}
|
||||
EventEmitter.removeListener(CHANGE_PASSCODE_EMITTER, listener);
|
||||
};
|
||||
}, []);
|
||||
|
|
|
@ -13,16 +13,18 @@ export const RoomSettings = ({
|
|||
isTeam,
|
||||
setValue,
|
||||
createChannelPermission,
|
||||
createPrivateChannelPermission
|
||||
createPrivateChannelPermission,
|
||||
e2eEnabledDefaultPrivateRooms
|
||||
}: {
|
||||
isTeam: boolean;
|
||||
setValue: UseFormSetValue<IFormData>;
|
||||
createChannelPermission: boolean;
|
||||
createPrivateChannelPermission: boolean;
|
||||
e2eEnabledDefaultPrivateRooms: boolean;
|
||||
}) => {
|
||||
const [type, setType] = useState(true);
|
||||
const [readOnly, setReadOnly] = useState(false);
|
||||
const [encrypted, setEncrypted] = useState(false);
|
||||
const [encrypted, setEncrypted] = useState(e2eEnabledDefaultPrivateRooms);
|
||||
const [broadcast, setBroadcast] = useState(false);
|
||||
|
||||
const { encryptionEnabled } = useAppSelector(state => ({
|
||||
|
|
|
@ -68,13 +68,29 @@ export interface IFormData {
|
|||
const CreateChannelView = () => {
|
||||
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 {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
setValue
|
||||
} = 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'>>();
|
||||
|
@ -84,15 +100,6 @@ const CreateChannelView = () => {
|
|||
const { colors } = useTheme();
|
||||
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(() => {
|
||||
sendLoadingEvent({ visible: isFetching });
|
||||
}, [isFetching]);
|
||||
|
@ -154,6 +161,7 @@ const CreateChannelView = () => {
|
|||
createPrivateChannelPermission={createPrivateChannelPermission}
|
||||
isTeam={isTeam}
|
||||
setValue={setValue}
|
||||
e2eEnabledDefaultPrivateRooms={e2eEnabledDefaultPrivateRooms}
|
||||
/>
|
||||
</View>
|
||||
{users.length > 0 ? (
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Base64 } from 'js-base64';
|
|||
import React from 'react';
|
||||
import { BackHandler, Image, Keyboard, StyleSheet, Text, View } from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import { connect } from 'react-redux';
|
||||
import parse from 'url-parse';
|
||||
|
||||
|
@ -88,9 +87,6 @@ interface ISubmitParams {
|
|||
class NewServerView extends React.Component<INewServerViewProps, INewServerViewState> {
|
||||
constructor(props: INewServerViewProps) {
|
||||
super(props);
|
||||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
this.setHeader();
|
||||
|
||||
this.state = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { sha256 } from 'js-sha256';
|
||||
import React from 'react';
|
||||
import { Keyboard, Text } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { deleteAccount } from '../../../../actions/login';
|
||||
|
@ -18,7 +17,6 @@ import sharedStyles from '../../../Styles';
|
|||
export function DeleteAccountActionSheetContent(): React.ReactElement {
|
||||
const { hideActionSheet, showActionSheet } = useActionSheet();
|
||||
const dispatch = useDispatch();
|
||||
const insets = useSafeAreaInsets();
|
||||
const { colors } = useTheme();
|
||||
|
||||
const handleDeleteAccount = async (password: string) => {
|
||||
|
@ -40,8 +38,7 @@ export function DeleteAccountActionSheetContent(): React.ReactElement {
|
|||
removedRooms={removedRooms}
|
||||
password={sha256(password)}
|
||||
/>
|
||||
),
|
||||
headerHeight: 225 + insets.bottom
|
||||
)
|
||||
});
|
||||
}, 250); // timeout for hide effect
|
||||
} else if (error.data.errorType === 'error-invalid-password') {
|
||||
|
|
|
@ -237,8 +237,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
|||
}}
|
||||
onCancel={this.props.hideActionSheet}
|
||||
/>
|
||||
),
|
||||
headerHeight: 225
|
||||
)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -417,8 +416,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
|||
deleteOwnAccount = () => {
|
||||
logEvent(events.DELETE_OWN_ACCOUNT);
|
||||
this.props.showActionSheet({
|
||||
children: <DeleteAccountActionSheetContent />,
|
||||
headerHeight: 225
|
||||
children: <DeleteAccountActionSheetContent />
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1109,7 +1109,7 @@ class RoomActionsView extends React.Component<IRoomActionsViewProps, IRoomAction
|
|||
{teamId && isTeamRoom({ teamId, joined }) ? (
|
||||
<>
|
||||
<List.Item
|
||||
title='Teams'
|
||||
title='Channels'
|
||||
onPress={() => {
|
||||
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
||||
if (isMasterDetail) {
|
||||
|
|
|
@ -857,7 +857,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
|||
children: (
|
||||
<ReactionPicker message={selectedMessage} onEmojiSelected={this.onReactionPress} reactionClose={this.onReactionClose} />
|
||||
),
|
||||
snaps: [400],
|
||||
snaps: ['50%'],
|
||||
enableContentPanningGesture: false
|
||||
});
|
||||
}, 100);
|
||||
|
|
|
@ -4,8 +4,6 @@ import { StyleSheet, Text, TextInputProps, TouchableOpacity, TouchableOpacityPro
|
|||
import I18n from '../../../i18n';
|
||||
import sharedStyles from '../../Styles';
|
||||
import { CustomIcon } from '../../../containers/CustomIcon';
|
||||
import { isIOS, isTablet } from '../../../lib/methods/helpers';
|
||||
import { useOrientation } from '../../../dimensions';
|
||||
import { useTheme } from '../../../theme';
|
||||
import SearchHeader from '../../../containers/SearchHeader';
|
||||
|
||||
|
@ -20,9 +18,11 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
title: {
|
||||
flexShrink: 1,
|
||||
fontSize: 16,
|
||||
...sharedStyles.textSemibold
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
upsideDown: {
|
||||
|
@ -55,10 +55,6 @@ const Header = React.memo(
|
|||
onPress
|
||||
}: IRoomHeader) => {
|
||||
const { colors } = useTheme();
|
||||
const { isLandscape } = useOrientation();
|
||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
||||
const titleFontSize = 16 * scale;
|
||||
const subTitleFontSize = 14 * scale;
|
||||
|
||||
if (showSearchHeader) {
|
||||
return <SearchHeader onSearchChangeText={onSearchChangeText} testID='rooms-list-view-search-input' />;
|
||||
|
@ -77,7 +73,7 @@ const Header = React.memo(
|
|||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={onPress} testID='rooms-list-header-server-dropdown-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}
|
||||
</Text>
|
||||
<CustomIcon
|
||||
|
@ -90,7 +86,7 @@ const Header = React.memo(
|
|||
{subtitle ? (
|
||||
<Text
|
||||
testID='rooms-list-header-server-subtitle'
|
||||
style={[styles.subtitle, { color: colors.auxiliaryText, fontSize: subTitleFontSize }]}
|
||||
style={[styles.subtitle, { color: colors.auxiliaryText }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{subtitle}
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { BackHandler, FlatList, Keyboard, NativeEventSubscription, RefreshControl, Text, View } from 'react-native';
|
||||
import { batch, connect } from 'react-redux';
|
||||
import { dequal } from 'dequal';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
@ -56,7 +55,8 @@ import {
|
|||
isRead,
|
||||
debounce,
|
||||
isIOS,
|
||||
isTablet
|
||||
isTablet,
|
||||
compareServerVersion
|
||||
} from '../../lib/methods/helpers';
|
||||
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
|
||||
import { Services } from '../../lib/services';
|
||||
|
@ -104,6 +104,7 @@ interface IRoomsListViewProps {
|
|||
createPublicChannelPermission: [];
|
||||
createPrivateChannelPermission: [];
|
||||
createDiscussionPermission: [];
|
||||
serverVersion: string;
|
||||
}
|
||||
|
||||
interface IRoomsListViewState {
|
||||
|
@ -216,7 +217,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
|||
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
|
||||
}
|
||||
this.unsubscribeFocus = navigation.addListener('focus', () => {
|
||||
Orientation.unlockAllOrientations();
|
||||
this.animated = true;
|
||||
// Check if there were changes with sort preference, then call getSubscription to remount the list
|
||||
if (this.sortPreferencesChanged) {
|
||||
|
@ -706,9 +706,11 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
|||
|
||||
toggleRead = async (rid: string, tIsRead: boolean) => {
|
||||
logEvent(tIsRead ? events.RL_UNREAD_CHANNEL : events.RL_READ_CHANNEL);
|
||||
const { serverVersion } = this.props;
|
||||
try {
|
||||
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) {
|
||||
const subCollection = db.get('subscriptions');
|
||||
|
@ -718,6 +720,9 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
|||
await subRecord.update(sub => {
|
||||
sub.alert = tIsRead;
|
||||
sub.unread = 0;
|
||||
if (includeThreads) {
|
||||
sub.tunread = [];
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
log(e);
|
||||
|
@ -955,7 +960,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
|
|||
showAvatar,
|
||||
displayMode
|
||||
} = this.props;
|
||||
const id = getUidDirectMessage(item);
|
||||
const id = item.search && item.t === 'd' ? item._id : getUidDirectMessage(item);
|
||||
const swipeEnabled = this.isSwipeEnabled(item);
|
||||
|
||||
return (
|
||||
|
@ -1067,7 +1072,8 @@ const mapStateToProps = (state: IApplicationState) => ({
|
|||
createDirectMessagePermission: state.permissions['create-d'],
|
||||
createPublicChannelPermission: state.permissions['create-c'],
|
||||
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))));
|
||||
|
|
|
@ -2,7 +2,6 @@ import isEmpty from 'lodash/isEmpty';
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Modal from 'react-native-modal';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
import useDeepCompareEffect from 'use-deep-compare-effect';
|
||||
|
||||
|
@ -10,7 +9,7 @@ import { PasscodeEnter } from '../containers/Passcode';
|
|||
import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants';
|
||||
import { CustomIcon } from '../containers/CustomIcon';
|
||||
import { useTheme } from '../theme';
|
||||
import { hasNotch, isTablet } from '../lib/methods/helpers';
|
||||
import { hasNotch } from '../lib/methods/helpers';
|
||||
import EventEmitter from '../lib/methods/helpers/events';
|
||||
|
||||
interface IData {
|
||||
|
@ -46,14 +45,8 @@ const ScreenLockedView = (): JSX.Element => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
const listener = EventEmitter.addEventListener(LOCAL_AUTHENTICATE_EMITTER, showScreenLock);
|
||||
return () => {
|
||||
if (!isTablet) {
|
||||
Orientation.unlockAllOrientations();
|
||||
}
|
||||
EventEmitter.removeListener(LOCAL_AUTHENTICATE_EMITTER, listener);
|
||||
};
|
||||
}, []);
|
||||
|
|
|
@ -2,7 +2,6 @@ import { useNavigation } from '@react-navigation/native';
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { ScrollView, StyleSheet, Text } from 'react-native';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import * as yup from 'yup';
|
||||
|
@ -18,7 +17,7 @@ import I18n from '../i18n';
|
|||
import KeyboardView from '../containers/KeyboardView';
|
||||
import { getUserSelector } from '../selectors/login';
|
||||
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 sharedStyles from './Styles';
|
||||
import { Services } from '../lib/services';
|
||||
|
@ -56,9 +55,6 @@ const SetUsernameView = () => {
|
|||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({ title: server });
|
||||
if (!isTablet) {
|
||||
Orientation.lockToPortrait();
|
||||
}
|
||||
}, [navigation, server]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -122,9 +122,5 @@ export default StyleSheet.create({
|
|||
},
|
||||
inputLastChild: {
|
||||
marginBottom: 15
|
||||
},
|
||||
notchLandscapeContainer: {
|
||||
marginTop: -34,
|
||||
paddingHorizontal: 30
|
||||
}
|
||||
});
|
||||
|
|
|
@ -520,10 +520,10 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
|
|||
return <BackgroundContainer loading />;
|
||||
}
|
||||
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) {
|
||||
return <BackgroundContainer text={I18n.t('No_team_channels_found')} />;
|
||||
return <BackgroundContainer text={I18n.t('No_channels_in_team')} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -9,18 +9,20 @@ import {
|
|||
TTextMatcher,
|
||||
tapAndWaitFor,
|
||||
navigateToRoom,
|
||||
mockMessage
|
||||
mockMessage,
|
||||
tryTapping
|
||||
} from '../../helpers/app';
|
||||
import { createRandomRoom, createRandomUser } from '../../helpers/data_setup';
|
||||
|
||||
describe('Threads', () => {
|
||||
let room: string;
|
||||
let textMatcher: TTextMatcher;
|
||||
let alertButtonType: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const user = await createRandomUser();
|
||||
({ name: room } = await createRandomRoom(user));
|
||||
({ textMatcher } = platformTypes[device.getPlatform()]);
|
||||
({ textMatcher, alertButtonType } = platformTypes[device.getPlatform()]);
|
||||
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
||||
await navigateToLogin();
|
||||
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 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 { 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 random from '../../helpers/random';
|
||||
|
||||
const roomId = '64b846e4760e618aa9f91ab7';
|
||||
|
||||
function getIndex() {
|
||||
if (device.getPlatform() === 'android') {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sendMessageOnTranslationTestRoom = async (msg: string): Promise<{ user: ITestUser; msgId: string }> => {
|
||||
const user = await createRandomUser();
|
||||
const api = await initApi(user.username, user.password);
|
||||
|
@ -36,22 +43,37 @@ async function navigateToRoom(roomName: string) {
|
|||
.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) {
|
||||
return waitFor(element(by.id(id)))
|
||||
.toBeVisible()
|
||||
.withTimeout(5000);
|
||||
.withTimeout(10000);
|
||||
}
|
||||
|
||||
export function waitForVisibleTextMatcher(msg: string, textMatcher: TTextMatcher) {
|
||||
return waitFor(element(by[textMatcher](msg)).atIndex(0))
|
||||
.toExist()
|
||||
.withTimeout(5000);
|
||||
.withTimeout(10000);
|
||||
}
|
||||
|
||||
export function waitForNotVisible(id: string) {
|
||||
return waitFor(element(by.id(id)))
|
||||
.not.toBeVisible()
|
||||
.withTimeout(5000);
|
||||
.withTimeout(10000);
|
||||
}
|
||||
|
||||
describe('Auto Translate', () => {
|
||||
|
@ -97,6 +119,7 @@ describe('Auto Translate', () => {
|
|||
});
|
||||
|
||||
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(attachmentMessage[languages.default] as string, textMatcher);
|
||||
});
|
||||
|
@ -141,6 +164,7 @@ describe('Auto Translate', () => {
|
|||
});
|
||||
|
||||
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(attachmentMessage[languages.translated] as string, textMatcher);
|
||||
});
|
||||
|
@ -148,6 +172,7 @@ describe('Auto Translate', () => {
|
|||
it('should see new message translated', async () => {
|
||||
const randomMatcher = random();
|
||||
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 deleteMessageOnTranslationTestRoom(data);
|
||||
});
|
||||
|
|
|
@ -1760,7 +1760,7 @@
|
|||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
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_FAST_MATH = YES;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||
|
@ -1799,7 +1799,7 @@
|
|||
INFOPLIST_FILE = NotificationService/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
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;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.40.0</string>
|
||||
<string>4.41.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.40.0</string>
|
||||
<string>4.41.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>KeychainGroup</key>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "rocket-chat-reactnative",
|
||||
"version": "4.40.0",
|
||||
"version": "4.41.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "react-native start",
|
||||
|
@ -128,11 +128,12 @@
|
|||
"react-native-safe-area-context": "3.2.0",
|
||||
"react-native-screens": "3.13.1",
|
||||
"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-slowlog": "^1.0.2",
|
||||
"react-native-svg": "^13.8.0",
|
||||
"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-webview": "11.26.1",
|
||||
"react-redux": "^8.0.5",
|
||||
|
|
59
yarn.lock
59
yarn.lock
|
@ -3139,6 +3139,13 @@
|
|||
dependencies:
|
||||
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":
|
||||
version "7.9.6"
|
||||
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"
|
||||
ieee754 "^1.1.4"
|
||||
|
||||
buffer@^5.5.0:
|
||||
buffer@^5.4.3, buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
|
@ -9594,9 +9601,11 @@ damerau-levenshtein@^1.0.7:
|
|||
integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
|
||||
|
||||
date-fns@^2.29.3:
|
||||
version "2.29.3"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
|
||||
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
|
||||
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.21.0"
|
||||
|
||||
dayjs@^1.8.15:
|
||||
version "1.11.7"
|
||||
|
@ -17044,6 +17053,11 @@ punycode@^2.1.0:
|
|||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
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:
|
||||
version "3.2.0"
|
||||
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"
|
||||
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"
|
||||
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:
|
||||
base64-js "^1.3.0"
|
||||
hex-lite "^1.5.0"
|
||||
|
@ -17669,6 +17683,13 @@ react-native-ui-lib@RocketChat/react-native-ui-lib#ef50151b8d9c1627ef527c620a147
|
|||
tinycolor2 "^1.4.2"
|
||||
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:
|
||||
version "9.1.0"
|
||||
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"
|
||||
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:
|
||||
version "0.14.4"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
@ -18599,6 +18625,11 @@ semver@7.3.2:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
|
||||
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:
|
||||
version "6.3.0"
|
||||
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"
|
||||
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:
|
||||
version "3.7.3"
|
||||
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"
|
||||
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:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
|
|
Loading…
Reference in New Issue