From 9d0b6590978e9d3bf5bb26c131b437215170b471 Mon Sep 17 00:00:00 2001 From: Samay Kothari <2001samay@gmail.com> Date: Wed, 4 May 2022 01:22:56 +0530 Subject: [PATCH 1/4] [FIX] Messages showing URL preview on Room Actions (#4065) * using database query instead of Rocketchat search api to get message search results * getting search data from server then converting it into the format as required for message rendering * fixing some redundant changes * remove ts-ignore * removing redundant statements * url preview is visible when we see messages in pinned, mentions and starred messages Co-authored-by: Gerzon Z --- app/views/MessagesView/index.tsx | 21 +++++++++++++++++++-- app/views/SearchMessagesView/index.tsx | 21 +++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/app/views/MessagesView/index.tsx b/app/views/MessagesView/index.tsx index b63366a4d..714e4ef3d 100644 --- a/app/views/MessagesView/index.tsx +++ b/app/views/MessagesView/index.tsx @@ -22,7 +22,7 @@ import { ChatsStackParamList } from '../../stacks/types'; import { ISubscription, SubscriptionType } from '../../definitions/ISubscription'; import { IEmoji } from '../../definitions/IEmoji'; import { IRoomInfoParam } from '../SearchMessagesView'; -import { TMessageModel } from '../../definitions'; +import { TMessageModel, IUrl } from '../../definitions'; import { Services } from '../../lib/services'; interface IMessagesViewProps { @@ -280,8 +280,25 @@ class MessagesView extends React.Component { try { const result = await this.content.fetchFunc(); if (result.success) { + const urlRenderMessages = result.messages?.map((message: any) => { + if (message.urls && message.urls.length > 0) { + message.urls = message.urls?.map((url: any, index: any) => { + if (url.meta) { + return { + _id: index, + title: url.meta.pageTitle, + description: url.meta.ogDescription, + image: url.meta.ogImage, + url: url.url + } as IUrl; + } + return {} as IUrl; + }); + } + return message; + }); this.setState({ - messages: [...messages, ...result.messages], + messages: [...messages, ...urlRenderMessages], total: result.total, loading: false }); diff --git a/app/views/SearchMessagesView/index.tsx b/app/views/SearchMessagesView/index.tsx index c286d5477..2e6252bee 100644 --- a/app/views/SearchMessagesView/index.tsx +++ b/app/views/SearchMessagesView/index.tsx @@ -32,6 +32,7 @@ import styles from './styles'; import { InsideStackParamList, ChatsStackParamList } from '../../stacks/types'; import { IEmoji } from '../../definitions/IEmoji'; import { compareServerVersion } from '../../lib/methods/helpers/compareServerVersion'; +import { IUrl } from '../../definitions'; import { Services } from '../../lib/services'; const QUERY_SIZE = 50; @@ -155,9 +156,25 @@ class SearchMessagesView extends React.Component { + if (message.urls && message.urls.length > 0) { + message.urls = message.urls?.map((url, index) => { + if (url.meta) { + return { + _id: index, + title: url.meta.pageTitle, + description: url.meta.ogDescription, + image: url.meta.ogImage, + url: url.url + } as IUrl; + } + return {} as IUrl; + }); + } + return message; + }); + return urlRenderMessages; } - return []; }; getMessages = async (searchText: string, debounced?: boolean) => { From 6eb2179088c9ca908f6647bb385595928bd2a4d6 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Tue, 3 May 2022 17:06:49 -0300 Subject: [PATCH 2/4] Chore: Update ua-parser-js from 0.7.18 to 0.7.24 and set resolution (#4058) * update ua-parser and set resolution * update ua-parser and set resolution * wip --- package.json | 5 ++++- yarn.lock | 13 ++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index c67937ef9..565476491 100644 --- a/package.json +++ b/package.json @@ -131,11 +131,14 @@ "rn-fetch-blob": "0.12.0", "rn-root-view": "1.0.3", "semver": "7.3.5", - "ua-parser-js": "0.7.28", + "ua-parser-js": "^0.7.24", "url-parse": "1.5.6", "use-deep-compare-effect": "1.6.1", "xregexp": "5.0.2" }, + "resolutions": { + "ua-parser-js": "^0.7.24" + }, "devDependencies": { "@babel/core": "^7.12.9", "@babel/eslint-parser": "^7.14.7", diff --git a/yarn.lock b/yarn.lock index 08b292bc3..23b1948c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17444,15 +17444,10 @@ typical@^5.2.0: resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== -ua-parser-js@0.7.28: - version "0.7.28" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" - integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== - -ua-parser-js@^0.7.18: - version "0.7.21" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== +ua-parser-js@^0.7.18, ua-parser-js@^0.7.24: + version "0.7.31" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" + integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== uglify-es@^3.1.9: version "3.3.9" From 2077671761e9efb212bdec38a090123cb35e44e1 Mon Sep 17 00:00:00 2001 From: Danish Ahmed Mirza <77742477+try-catch-stack@users.noreply.github.com> Date: Wed, 4 May 2022 01:45:16 +0530 Subject: [PATCH 3/4] Chore: Migrate containers/Loading to `reanimated` v2 and Hooks (#4073) * Chore : Clean `Loading` component and migrate to reanimated v2 * Remove theme prop * Use colors from useTheme and PixelRatio for image Co-authored-by: Gleidson Daniel Silva --- app/containers/Loading.tsx | 167 ++++++++++------------------------ app/views/ShareView/index.tsx | 2 +- 2 files changed, 51 insertions(+), 118 deletions(-) diff --git a/app/containers/Loading.tsx b/app/containers/Loading.tsx index c457b0401..be7e68513 100644 --- a/app/containers/Loading.tsx +++ b/app/containers/Loading.tsx @@ -1,8 +1,17 @@ -import React from 'react'; -import { Animated, Modal, StyleSheet, View } from 'react-native'; +import React, { useEffect } from 'react'; +import { Modal, StyleSheet, View, PixelRatio } from 'react-native'; +import Animated, { + cancelAnimation, + Extrapolate, + interpolate, + useAnimatedStyle, + useSharedValue, + withRepeat, + withSequence, + withTiming +} from 'react-native-reanimated'; -import { TSupportedThemes, withTheme } from '../theme'; -import { themes } from '../lib/constants'; +import { useTheme } from '../theme'; const styles = StyleSheet.create({ container: { @@ -11,130 +20,54 @@ const styles = StyleSheet.create({ justifyContent: 'center' }, image: { - width: 100, - height: 100, + width: PixelRatio.get() * 40, + height: PixelRatio.get() * 40, resizeMode: 'contain' } }); interface ILoadingProps { visible: boolean; - theme?: TSupportedThemes; } -interface ILoadingState { - scale: Animated.Value; - opacity: Animated.Value; -} - -class Loading extends React.PureComponent { - state = { - scale: new Animated.Value(1), - opacity: new Animated.Value(0) - }; - - private opacityAnimation?: Animated.CompositeAnimation; - - private scaleAnimation?: Animated.CompositeAnimation; - - componentDidMount() { - const { opacity, scale } = this.state; - const { visible } = this.props; - - this.opacityAnimation = Animated.timing(opacity, { - toValue: 1, - duration: 200, - useNativeDriver: true - }); - this.scaleAnimation = Animated.loop( - Animated.sequence([ - Animated.timing(scale, { - toValue: 0, - duration: 1000, - useNativeDriver: true - }), - Animated.timing(scale, { - toValue: 1, - duration: 1000, - useNativeDriver: true - }) - ]) - ); +const Loading = ({ visible }: ILoadingProps): React.ReactElement => { + const opacity = useSharedValue(0); + const scale = useSharedValue(1); + const { colors } = useTheme(); + useEffect(() => { if (visible) { - this.startAnimations(); + opacity.value = withTiming(1, { + duration: 200 + }); + scale.value = withRepeat(withSequence(withTiming(0, { duration: 1000 }), withTiming(1, { duration: 1000 })), -1); } - } + return () => { + cancelAnimation(scale); + }; + }, [opacity, scale, visible]); - componentDidUpdate(prevProps: ILoadingProps) { - const { visible } = this.props; - if (visible && visible !== prevProps.visible) { - this.startAnimations(); - } - } + const animatedOpacity = useAnimatedStyle(() => ({ + opacity: interpolate(opacity.value, [0, 1], [0, colors.backdropOpacity], Extrapolate.CLAMP) + })); + const animatedScale = useAnimatedStyle(() => ({ transform: [{ scale: interpolate(scale.value, [0, 0.5, 1], [1, 1.1, 1]) }] })); - componentWillUnmount() { - if (this.opacityAnimation && this.opacityAnimation.stop) { - this.opacityAnimation.stop(); - } - if (this.scaleAnimation && this.scaleAnimation.stop) { - this.scaleAnimation.stop(); - } - } + return ( + {}}> + + + + + + ); +}; - startAnimations() { - if (this.opacityAnimation && this.opacityAnimation.start) { - this.opacityAnimation.start(); - } - if (this.scaleAnimation && this.scaleAnimation.start) { - this.scaleAnimation.start(); - } - } - - render() { - const { opacity, scale } = this.state; - const { visible, theme } = this.props; - - const scaleAnimation = scale.interpolate({ - inputRange: [0, 0.5, 1], - outputRange: [1, 1.1, 1] - }); - - const opacityAnimation = opacity.interpolate({ - inputRange: [0, 1], - outputRange: [0, themes[theme!].backdropOpacity], - extrapolate: 'clamp' - }); - - return ( - {}}> - - - - - - ); - } -} - -export default withTheme(Loading); +export default Loading; diff --git a/app/views/ShareView/index.tsx b/app/views/ShareView/index.tsx index e98e11628..5dbd9c2e9 100644 --- a/app/views/ShareView/index.tsx +++ b/app/views/ShareView/index.tsx @@ -361,7 +361,7 @@ class ShareView extends Component { {this.renderContent()} - + ); } From 305f360b4029011d5acfe94237f6ea40ede08619 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Tue, 3 May 2022 17:29:00 -0300 Subject: [PATCH 4/4] [FEAT]: Request current passcode before enter "Screen lock" screen (#4052) * move auth to handleLocalAuthentication function * add support to close ScreenLockedView * create useServer hook * add check to verify if you have a password before entering the screen * fix import --- app/lib/methods/useServer.ts | 29 ++++++++++++++++++++++ app/utils/localAuthentication.ts | 34 +++++++++++++++----------- app/views/ScreenLockedView.tsx | 40 ++++++++++++++++++++++++++----- app/views/SecurityPrivacyView.tsx | 40 +++++++++++++++++++------------ 4 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 app/lib/methods/useServer.ts diff --git a/app/lib/methods/useServer.ts b/app/lib/methods/useServer.ts new file mode 100644 index 000000000..c282a5cf6 --- /dev/null +++ b/app/lib/methods/useServer.ts @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { IApplicationState, TServerModel } from '../../definitions'; +import database from '../database'; + +export default function useServer() { + const [server, setServer] = useState(null); + const shareServer = useSelector((state: IApplicationState) => state.share.server.server); + const appServer = useSelector((state: IApplicationState) => state.server.server); + + useEffect(() => { + async function init() { + const serversDB = database.servers; + const serversCollection = serversDB.get('servers'); + let serverInfo = null; + try { + serverInfo = await serversCollection.find(shareServer || appServer); + setServer(serverInfo); + } catch { + setServer(serverInfo); + } + } + + init(); + }, []); + + return [server]; +} diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index f094eee8c..ec72efbb1 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -50,11 +50,13 @@ export const saveLastLocalAuthenticationSession = async ( export const resetAttempts = (): Promise => AsyncStorage.multiRemove([LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY]); -const openModal = (hasBiometry: boolean) => - new Promise(resolve => { +const openModal = (hasBiometry: boolean, force?: boolean) => + new Promise((resolve, reject) => { EventEmitter.emit(LOCAL_AUTHENTICATE_EMITTER, { submit: () => resolve(), - hasBiometry + hasBiometry, + force, + cancel: () => reject() }); }); @@ -100,6 +102,20 @@ export const checkHasPasscode = async ({ force = true }: { force?: boolean }): P return Promise.resolve(); }; +export const handleLocalAuthentication = async (canCloseModal = false) => { + // let hasBiometry = false; + let hasBiometry = UserPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? false; + + // if biometry is enabled on the app + if (hasBiometry) { + const isEnrolled = await LocalAuthentication.isEnrolledAsync(); + hasBiometry = isEnrolled; + } + + // Authenticate + await openModal(hasBiometry, canCloseModal); +}; + export const localAuthenticate = async (server: string): Promise => { const serversDB = database.servers; const serversCollection = serversDB.get('servers'); @@ -136,17 +152,7 @@ export const localAuthenticate = async (server: string): Promise => { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); - // let hasBiometry = false; - let hasBiometry = UserPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? false; - - // if biometry is enabled on the app - if (hasBiometry) { - const isEnrolled = await LocalAuthentication.isEnrolledAsync(); - hasBiometry = isEnrolled; - } - - // Authenticate - await openModal(hasBiometry); + await handleLocalAuthentication(); // set isLocalAuthenticated to true store.dispatch(setLocalAuthenticated(true)); diff --git a/app/views/ScreenLockedView.tsx b/app/views/ScreenLockedView.tsx index b4306688a..7cf21abed 100644 --- a/app/views/ScreenLockedView.tsx +++ b/app/views/ScreenLockedView.tsx @@ -1,22 +1,37 @@ -import React, { useEffect, useState } from 'react'; -import Modal from 'react-native-modal'; -import useDeepCompareEffect from 'use-deep-compare-effect'; 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'; -import EventEmitter from '../utils/events'; -import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants'; -import { isTablet } from '../utils/deviceInfo'; 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 '../utils/deviceInfo'; +import EventEmitter from '../utils/events'; interface IData { submit?: () => void; + cancel?: () => void; hasBiometry?: boolean; + force?: boolean; } +const styles = StyleSheet.create({ + close: { + position: 'absolute', + top: hasNotch ? 50 : 30, + left: 15 + } +}); + const ScreenLockedView = (): JSX.Element => { const [visible, setVisible] = useState(false); const [data, setData] = useState({}); + const { colors } = useTheme(); useDeepCompareEffect(() => { if (!isEmpty(data)) { @@ -51,6 +66,14 @@ const ScreenLockedView = (): JSX.Element => { setData({}); }; + const onCancel = () => { + const { cancel } = data; + if (cancel) { + cancel(); + } + setData({}); + }; + return ( { animationIn='fadeIn' animationOut='fadeOut'> + {data?.force ? ( + + + + ) : null} ); }; diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx index f632ce55d..5487819d1 100644 --- a/app/views/SecurityPrivacyView.tsx +++ b/app/views/SecurityPrivacyView.tsx @@ -1,24 +1,26 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import { StackNavigationProp } from '@react-navigation/stack'; import React, { useEffect, useState } from 'react'; import { Switch } from 'react-native'; -import { StackNavigationProp } from '@react-navigation/stack'; -import AsyncStorage from '@react-native-community/async-storage'; import { useSelector } from 'react-redux'; -import StatusBar from '../containers/StatusBar'; import * as List from '../containers/List'; -import I18n from '../i18n'; -import { - logEvent, - events, - toggleCrashErrorsReport, - toggleAnalyticsEventsReport, - getReportCrashErrorsValue, - getReportAnalyticsEventsValue -} from '../utils/log'; import SafeAreaView from '../containers/SafeAreaView'; -import { ANALYTICS_EVENTS_KEY, CRASH_REPORT_KEY, isFDroidBuild, SWITCH_TRACK_COLOR } from '../lib/constants'; -import { SettingsStackParamList } from '../stacks/types'; +import StatusBar from '../containers/StatusBar'; import { IApplicationState } from '../definitions'; +import I18n from '../i18n'; +import { ANALYTICS_EVENTS_KEY, CRASH_REPORT_KEY, isFDroidBuild, SWITCH_TRACK_COLOR } from '../lib/constants'; +import useServer from '../lib/methods/useServer'; +import { SettingsStackParamList } from '../stacks/types'; +import { handleLocalAuthentication } from '../utils/localAuthentication'; +import { + events, + getReportAnalyticsEventsValue, + getReportCrashErrorsValue, + logEvent, + toggleAnalyticsEventsReport, + toggleCrashErrorsReport +} from '../utils/log'; interface ISecurityPrivacyViewProps { navigation: StackNavigationProp; @@ -27,6 +29,7 @@ interface ISecurityPrivacyViewProps { const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Element => { const [crashReportState, setCrashReportState] = useState(getReportCrashErrorsValue()); const [analyticsEventsState, setAnalyticsEventsState] = useState(getReportAnalyticsEventsValue()); + const [server] = useServer(); const e2eEnabled = useSelector((state: IApplicationState) => state.settings.E2E_Enable); @@ -56,6 +59,13 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele navigation.navigate(screen); }; + const navigateToScreenLockConfigView = async () => { + if (server?.autoLock) { + await handleLocalAuthentication(true); + } + navigateToScreen('ScreenLockConfigView'); + }; + return ( @@ -76,7 +86,7 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele navigateToScreen('ScreenLockConfigView')} + onPress={navigateToScreenLockConfigView} testID='security-privacy-view-screen-lock' />