Merge 4.14.1 into single-server (#2892)

* [FIX] RoomItem using deprecated animated event signature (#2771)

* [FIX] Server autocomplete text breaking line (#2774)

* [FIX] ServerDropdown flashing bigger server icon (#2775)

* [FIX] ServerDropdown flashing bigger server icon

* Remove unused logo and update image path where needed

* Minor tweak

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Rooms list not being updated on some cases (#2765)

* Request subscriptions on RoomsListView.constructor

* Removes opened rooms from last message persisting

* Change server reducer

* Prevent undefined ids causing query error

* [FIX] Share Extension hitting memory limit on iOS (#2788)

* [FIX] Disallow swipe to dismiss on share extension

* Limit query to 20 and clean up props

* Remove rn-extension-share branch pointer

* Test new branch

* Remove branch

* [IMPROVEMENT] Threads layout tweaks (#2686)

* improvement: Thread Details

* fix: re-render Thread Messages Item

* fix: update snapshots

* improve: thread details component

* fix: cast replies length

* improvement: format date of threads

* improvement: thread details styles

* fix: wrap text

* tests: update snapshot

* improvement: use same date format for all dates

* Icon size 24

* Remove date

* Remove prop drill

* Badge position

* Badge container tweak

* Fix inline style

* Move ThreadDetails to containers

* Update stories

* Fix lint

* Remove wrong prop

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Remove some migrations (#2792)

* Remove force rooms refresh

* Remove MMKV migration

* Bump version to 4.14.0 (#2797)

* [FIX] Messagebox tracking lost on pop gesture navigation (#2799)

* Use setTimeout instead of InteractionManager

* Update tracking lib

* [FIX] Back button closing activity when on root stack screen (#2804)

* Make hardware back button to behave as home button on root screens

* Remove unnecessary code

* Remove handleBackPress from OnboardingView

* Fix lint

* [i18n] Add missing German strings (#2715)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [NEW] Encrypted Discussions  (#2813)

* I18n key fix

* Add encrypted switch

* Remove unused i18n keys

* Add enabled to encryption reducer

* Show encrypted option on CreateDiscussionView only when e2e encryption is properly set

* Add localSearch and use it on search

* Use encrypted from parent channel

* Fix method calls as rest api with 2fa enabled

* Fix logout after reset keys

* Use encryption reducer instead of lib directly to check render

* Check for room type logic to display encryption option on create discussion

* Check toggle-room-e2e-encryption permission on RoomActionsView

* Check for encryption status instead of setting on server

* Fix

* Disable switch instead of hide it

* Fix spotlight for DMs

* Fix server test

* [FIX] Messagebox missing style for text color (#2786)

* Changing auxilaryTintColor

* Changed Placeholder color to BodyText color

* added color prop

* eslint changes

* used array for styles

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Update arabic (#2696)

* Update ar.js

* Update ar.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Workspace input without i18n (#2689)

* [FIX] Translation of strings in Login page

* Strings are added for translation.

fixes: #2620

* Add pt-BR

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Spotlight returning duplicated entries (#2805)

* Update rocketchat.js

* Updated search function

* Minor improvements

* Remove atIndex

* Add remove logic to remove duplicate data from response

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [CHORE] Refactor ServerItem (#2778)

* Updated ServerDropdown and ServerItem

* Added ServerItem stories

* Update ServerDropdown.js

* Updated ServerItem stories

* Updated ServerItem stories and ServerItem component

* Updated SelectServerView, ServerItem and ServerItem stories

* Updated ServerItem stories

* Updated ServerItem stories

* Update tests

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [DOCS] Updated Quick Start docs link in e2e/readme (#2802)

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [I18N] Add Turkish (#2793)

* Turkish language support added

* Update tr.js

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] Lint on #2793 (#2818)

* [I18N] Add missing german strings (#2689) (#2820)

* [I18N] Add missing italian strings (#2817)

* [FIX] Server version becoming null on server change (#2821)

* [FIX] Wrong styling on E2E encryption banner (#2767)

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner

* [FIX] Wrong styling on E2E encryption banner (#2767)

* Updated SortDropdown, ListHeader, ListItem and added stories for List.Item

* Updated SortDropdown

* Removed unused component

* Updated List.Item and stories

* Reverted unnecessary changes and updated ListItem stories

* Fix minor indentation

* Stop breaking Touch's default underlay color

* Fix indentation

* Remove falsy comparison from render

* Fix left icon

* Use List.Item on OmnichannelStatus

* Add missing separator

* Lint

* Fix sort dropdown

* Remove unnecessary styles

* Fix detox

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* [FIX] App Store using Experimental's app id (#2826)

* [FIX] Wrong username on push notifications (#2825)

* [FIX] Share extension memory issues on iOS (#2845)

* Remove unnecessary class prop

* Stop rendering servers when there's only one

* Map and alloc only necessary columns from query

* Fetch servers count instead of all servers records

* Fetch only needed servers

* Separators

* Remove renderContent

* Minor fix

* Refactor query

* Smaller avatars in memory

* Fix getItemLayout

* Add topic

* Load less pods

* tests

* Import only used functions from lodash

* Fix pods

* Import only used functions from semver

* Fix media sharing

* Update pods

* Disables preview and thumb on iOS

* Update expo-video-thumbnail

* Unnecessary change

* [FIX] Logout from other locations not prompting confirmation option (#2854)

* Fixed logout toast bug for the iOS

* Removing callToAction and replacing with confirmationText

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* Bump version to 4.14.1 (#2859)

Co-authored-by: Gerzon Z <gerzonzcanario@gmail.com>
Co-authored-by: Gerzon Z <gerzonc@icloud.com>
Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: phriedrich <info@phriedrich.de>
Co-authored-by: yash-rajpal <58601732+yash-rajpal@users.noreply.github.com>
Co-authored-by: Fazil Boudjelal <fazildiablou@hotmail.fr>
Co-authored-by: Sumukha Hegde <SUMUKHA214@GMAIL.COM>
Co-authored-by: Hakan YILMAZ <mukerrem.yilmaz@hotmail.com>
Co-authored-by: Vincenzo Esposito <aenon.esposito@gmail.com>
Co-authored-by: Arkadyuti Bandyopadhyay <bandyopadhyayarkadyuti@gmail.com>
This commit is contained in:
Diego Mello 2021-02-10 10:35:44 -03:00 committed by GitHub
parent d8cb5dfd6f
commit 322dabc762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1600 additions and 4765 deletions

File diff suppressed because it is too large Load Diff

View File

@ -144,7 +144,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode VERSIONCODE as Integer versionCode VERSIONCODE as Integer
versionName "4.14.0" versionName "4.14.1"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
if (!isFoss) { if (!isFoss) {
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import _ from 'lodash'; import range from 'lodash/range';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
@ -11,7 +11,7 @@ const SIZE_FULL = 16;
const Dots = React.memo(({ passcode, theme, length }) => ( const Dots = React.memo(({ passcode, theme, length }) => (
<View style={styles.dotsContainer}> <View style={styles.dotsContainer}>
{_.range(length).map((val) => { {range(length).map((val) => {
const lengthSup = (passcode.length >= val + 1); const lengthSup = (passcode.length >= val + 1);
const height = lengthSup ? SIZE_FULL : SIZE_EMPTY; const height = lengthSup ? SIZE_FULL : SIZE_EMPTY;
const width = lengthSup ? SIZE_FULL : SIZE_EMPTY; const width = lengthSup ? SIZE_FULL : SIZE_EMPTY;

View File

@ -2,7 +2,7 @@ import React, {
useState, forwardRef, useImperativeHandle, useRef useState, forwardRef, useImperativeHandle, useRef
} from 'react'; } from 'react';
import { Col, Row, Grid } from 'react-native-easy-grid'; import { Col, Row, Grid } from 'react-native-easy-grid';
import _ from 'lodash'; import range from 'lodash/range';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as Animatable from 'react-native-animatable'; import * as Animatable from 'react-native-animatable';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
@ -84,21 +84,21 @@ const Base = forwardRef(({
</Animatable.View> </Animatable.View>
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{_.range(1, 4).map(i => ( {range(1, 4).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{_.range(4, 7).map(i => ( {range(4, 7).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>
))} ))}
</Row> </Row>
<Row style={[styles.row, styles.buttonRow]}> <Row style={[styles.row, styles.buttonRow]}>
{_.range(7, 10).map(i => ( {range(7, 10).map(i => (
<Col key={i} style={styles.colButton}> <Col key={i} style={styles.colButton}>
<Button text={i} theme={theme} onPress={onPressNumber} /> <Button text={i} theme={theme} onPress={onPressNumber} />
</Col> </Col>

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { View, Text, InteractionManager } from 'react-native'; import { View, Text, InteractionManager } from 'react-native';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { sha256 } from 'js-sha256'; import { sha256 } from 'js-sha256';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
@ -47,7 +47,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
const sendEmail = () => RocketChat.sendEmailCode(); const sendEmail = () => RocketChat.sendEmailCode();
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
if (!_.isEmpty(data)) { if (!isEmpty(data)) {
setCode(''); setCode('');
setVisible(true); setVisible(true);
} else { } else {

View File

@ -1,4 +1,5 @@
import semver from 'semver'; import gte from 'semver/functions/gte';
import coerce from 'semver/functions/coerce';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
import database from '../database'; import database from '../database';
@ -33,7 +34,7 @@ export function getEnterpriseModules() {
return new Promise(async(resolve) => { return new Promise(async(resolve) => {
try { try {
const { version: serverVersion, server: serverId } = reduxStore.getState().server; const { version: serverVersion, server: serverId } = reduxStore.getState().server;
if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) { if (serverVersion && gte(coerce(serverVersion), '3.1.0')) {
// RC 3.1.0 // RC 3.1.0
const enterpriseModules = await this.methodCallWrapper('license:getModules'); const enterpriseModules = await this.methodCallWrapper('license:getModules');
if (enterpriseModules) { if (enterpriseModules) {

View File

@ -1,5 +1,5 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import semver from 'semver'; import lt from 'semver/functions/lt';
import orderBy from 'lodash/orderBy'; import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -91,7 +91,7 @@ export function getCustomEmojis() {
const updatedSince = await getUpdatedSince(allRecords); const updatedSince = await getUpdatedSince(allRecords);
// if server version is lower than 0.75.0, fetches from old api // if server version is lower than 0.75.0, fetches from old api
if (serverVersion && semver.lt(serverVersion, '0.75.0')) { if (serverVersion && lt(serverVersion, '0.75.0')) {
// RC 0.61.0 // RC 0.61.0
const result = await this.sdk.get('emoji-custom'); const result = await this.sdk.get('emoji-custom');

View File

@ -1,7 +1,7 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import semver from 'semver'; import lt from 'semver/functions/lt';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { orderBy } from 'lodash'; import orderBy from 'lodash/orderBy';
import database from '../database'; import database from '../database';
import log from '../../utils/log'; import log from '../../utils/log';
@ -79,7 +79,7 @@ export default function() {
const allRecords = await permissionsCollection.query().fetch(); const allRecords = await permissionsCollection.query().fetch();
// if server version is lower than 0.73.0, fetches from old api // if server version is lower than 0.73.0, fetches from old api
if (serverVersion && semver.lt(serverVersion, '0.73.0')) { if (serverVersion && lt(serverVersion, '0.73.0')) {
// RC 0.66.0 // RC 0.66.0
const result = await this.sdk.get('permissions.list'); const result = await this.sdk.get('permissions.list');
if (!result.success) { if (!result.success) {

View File

@ -1,5 +1,6 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import semver from 'semver'; import lt from 'semver/functions/lt';
import gte from 'semver/functions/gte';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
@ -11,7 +12,7 @@ export function subscribeUsersPresence() {
const serverVersion = reduxStore.getState().server.version; const serverVersion = reduxStore.getState().server.version;
// if server is lower than 1.1.0 // if server is lower than 1.1.0
if (serverVersion && semver.lt(serverVersion, '1.1.0')) { if (serverVersion && lt(serverVersion, '1.1.0')) {
if (this.activeUsersSubTimeout) { if (this.activeUsersSubTimeout) {
clearTimeout(this.activeUsersSubTimeout); clearTimeout(this.activeUsersSubTimeout);
this.activeUsersSubTimeout = false; this.activeUsersSubTimeout = false;
@ -36,11 +37,11 @@ export default async function getUsersPresence() {
const { user: loggedUser } = reduxStore.getState().login; const { user: loggedUser } = reduxStore.getState().login;
// if server is greather than or equal 1.1.0 // if server is greather than or equal 1.1.0
if (serverVersion && semver.gte(serverVersion, '1.1.0')) { if (serverVersion && gte(serverVersion, '1.1.0')) {
let params = {}; let params = {};
// if server is greather than or equal 3.0.0 // if server is greather than or equal 3.0.0
if (serverVersion && semver.gte(serverVersion, '3.0.0')) { if (serverVersion && gte(serverVersion, '3.0.0')) {
// if not have any id // if not have any id
if (!ids.length) { if (!ids.length) {
return; return;

View File

@ -1,5 +1,7 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import semver from 'semver'; import lt from 'semver/functions/lt';
import gte from 'semver/functions/gte';
import coerce from 'semver/functions/coerce';
import { import {
Rocketchat as RocketchatClient, Rocketchat as RocketchatClient,
settings as RocketChatSettings settings as RocketChatSettings
@ -132,7 +134,7 @@ const RocketChat = {
message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') }) message: I18n.t('Not_RC_Server', { contact: I18n.t('Contact_your_server_admin') })
}; };
} }
if (semver.lt(jsonRes.version, MIN_ROCKETCHAT_VERSION)) { if (lt(jsonRes.version, MIN_ROCKETCHAT_VERSION)) {
return { return {
success: false, success: false,
message: I18n.t('Invalid_server_version', { message: I18n.t('Invalid_server_version', {
@ -465,7 +467,7 @@ const RocketChat = {
// Force normalized params for 2FA starting RC 3.9.0. // Force normalized params for 2FA starting RC 3.9.0.
const serverVersion = reduxStore.getState().server.version; const serverVersion = reduxStore.getState().server.version;
if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.9.0')) { if (serverVersion && gte(coerce(serverVersion), '3.9.0')) {
const user = params.user ?? params.username; const user = params.user ?? params.username;
const password = params.password ?? params.ldapPass ?? params.crowdPassword; const password = params.password ?? params.ldapPass ?? params.crowdPassword;
params = { user, password }; params = { user, password };
@ -1372,7 +1374,7 @@ const RocketChat = {
}, },
readThreads(tmid) { readThreads(tmid) {
const serverVersion = reduxStore.getState().server.version; const serverVersion = reduxStore.getState().server.version;
if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.4.0')) { if (serverVersion && gte(coerce(serverVersion), '3.4.0')) {
// RC 3.4.0 // RC 3.4.0
return this.methodCallWrapper('readThreads', tmid); return this.methodCallWrapper('readThreads', tmid);
} }

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import _ from 'lodash'; import isEqual from 'lodash/isEqual';
import I18n from '../../i18n'; import I18n from '../../i18n';
import styles from './styles'; import styles from './styles';
@ -45,7 +45,7 @@ const formatMsg = ({
return `${ prefix }${ lastMessage.msg }`; return `${ prefix }${ lastMessage.msg }`;
}; };
const arePropsEqual = (oldProps, newProps) => _.isEqual(oldProps, newProps); const arePropsEqual = (oldProps, newProps) => isEqual(oldProps, newProps);
const LastMessage = React.memo(({ const LastMessage = React.memo(({
lastMessage, type, showLastMessage, username, alert, useRealName, theme lastMessage, type, showLastMessage, username, alert, useRealName, theme

View File

@ -34,7 +34,7 @@ import UserPreferences from '../lib/userPreferences';
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry'; import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib'; import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
import Navigation from '../lib/Navigation'; // import Navigation from '../lib/Navigation';
const getServer = state => state.server.server; const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);

View File

@ -2,7 +2,8 @@ import { put, takeLatest } from 'redux-saga/effects';
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import semver from 'semver'; import valid from 'semver/functions/valid';
import coerce from 'semver/functions/coerce';
import Navigation from '../lib/Navigation'; import Navigation from '../lib/Navigation';
import { SERVER } from '../actions/actionsTypes'; import { SERVER } from '../actions/actionsTypes';
@ -39,9 +40,9 @@ const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
return; return;
} }
let serverVersion = semver.valid(serverInfo.version); let serverVersion = valid(serverInfo.version);
if (!serverVersion) { if (!serverVersion) {
({ version: serverVersion } = semver.coerce(serverInfo.version)); ({ version: serverVersion } = coerce(serverInfo.version));
} }
const serversDB = database.servers; const serversDB = database.servers;

View File

@ -1,5 +1,5 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { isEmpty } from 'lodash'; import isEmpty from 'lodash/isEmpty';
const getUser = (state) => { const getUser = (state) => {
if (!isEmpty(state.share?.user)) { if (!isEmpty(state.share?.user)) {

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { ThemeContext } from '../theme'; import { ThemeContext } from '../theme';
import { import {
@ -21,7 +20,7 @@ import AuthenticationWebView from '../views/AuthenticationWebView';
// Outside // Outside
const Outside = createStackNavigator(); const Outside = createStackNavigator();
const _OutsideStack = (/*{ root }*/) => { const _OutsideStack = () => {
const { theme } = React.useContext(ThemeContext); const { theme } = React.useContext(ThemeContext);
return ( return (
@ -71,10 +70,6 @@ const mapStateToProps = state => ({
root: state.app.root root: state.app.root
}); });
_OutsideStack.propTypes = {
root: PropTypes.string
};
const OutsideStack = connect(mapStateToProps)(_OutsideStack); const OutsideStack = connect(mapStateToProps)(_OutsideStack);
// OutsideStackModal // OutsideStackModal

View File

@ -1,4 +1,5 @@
import semver from 'semver'; import lt from 'semver/functions/lt';
import coerce from 'semver/functions/coerce';
const formatUrl = (url, size, query) => `${ url }?format=png&size=${ size }${ query }`; const formatUrl = (url, size, query) => `${ url }?format=png&size=${ size }${ query }`;
@ -8,14 +9,12 @@ export const avatarURL = ({
let room; let room;
if (type === 'd') { if (type === 'd') {
room = text; room = text;
} else if (rid && !(serverVersion && semver.lt(semver.coerce(serverVersion), '3.6.0'))) { } else if (rid && !(serverVersion && lt(coerce(serverVersion), '3.6.0'))) {
room = `room/${ rid }`; room = `room/${ rid }`;
} else { } else {
room = `@${ text }`; room = `@${ text }`;
} }
const uriSize = size > 100 ? size : 100;
const { id, token } = user; const { id, token } = user;
let query = ''; let query = '';
if (id && token && blockUnauthenticatedAccess) { if (id && token && blockUnauthenticatedAccess) {
@ -30,8 +29,8 @@ export const avatarURL = ({
return avatar; return avatar;
} }
return formatUrl(`${ server }${ avatar }`, uriSize, query); return formatUrl(`${ server }${ avatar }`, size, query);
} }
return formatUrl(`${ server }/avatar/${ room }`, uriSize, query); return formatUrl(`${ server }/avatar/${ room }`, size, query);
}; };

View File

@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import useDeepCompareEffect from 'use-deep-compare-effect'; import useDeepCompareEffect from 'use-deep-compare-effect';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
@ -32,7 +32,7 @@ const ChangePasscodeView = React.memo(({ theme }) => {
const [data, setData] = useState({}); const [data, setData] = useState({});
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
if (!_.isEmpty(data)) { if (!isEmpty(data)) {
setVisible(true); setVisible(true);
} else { } else {
setVisible(false); setVisible(false);

View File

@ -5,7 +5,7 @@ import {
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import equal from 'deep-equal';
import { orderBy } from 'lodash'; import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import Touch from '../utils/touch'; import Touch from '../utils/touch';

View File

@ -6,7 +6,8 @@ import prompt from 'react-native-prompt-android';
import SHA256 from 'js-sha256'; import SHA256 from 'js-sha256';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import { isEqual, omit } from 'lodash'; import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import KeyboardView from '../../presentation/KeyboardView'; import KeyboardView from '../../presentation/KeyboardView';
@ -436,7 +437,7 @@ class ProfileView extends React.Component {
logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS); logEvent(events.PROFILE_LOGOUT_OTHER_LOCATIONS);
showConfirmationAlert({ showConfirmationAlert({
message: I18n.t('You_will_be_logged_out_from_other_locations'), message: I18n.t('You_will_be_logged_out_from_other_locations'),
callToAction: I18n.t('Logout'), confirmationText: I18n.t('Logout'),
onPress: async() => { onPress: async() => {
try { try {
await RocketChat.logoutOtherLocations(); await RocketChat.logoutOtherLocations();

View File

@ -4,8 +4,9 @@ import {
View, Text, Alert, Share, Switch View, Text, Alert, Share, Switch
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import semver from 'semver'; import lt from 'semver/functions/lt';
import coerce from 'semver/functions/coerce';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import { setLoading as setLoadingAction } from '../../actions/selectedUsers'; import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
@ -113,7 +114,7 @@ class RoomActionsView extends React.Component {
} catch (e) { } catch (e) {
log(e); log(e);
} }
} else if (room.t === 'd' && _.isEmpty(member)) { } else if (room.t === 'd' && isEmpty(member)) {
this.updateRoomMember(); this.updateRoomMember();
} }
@ -251,7 +252,7 @@ class RoomActionsView extends React.Component {
const { encrypted } = room; const { encrypted } = room;
const { serverVersion } = this.props; const { serverVersion } = this.props;
let hasPermission = false; let hasPermission = false;
if (serverVersion && semver.lt(semver.coerce(serverVersion), '3.11.0')) { if (serverVersion && lt(coerce(serverVersion), '3.11.0')) {
hasPermission = canEdit; hasPermission = canEdit;
} else { } else {
hasPermission = canToggleEncryption; hasPermission = canToggleEncryption;

View File

@ -9,7 +9,9 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import ImagePicker from 'react-native-image-crop-picker'; import ImagePicker from 'react-native-image-crop-picker';
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import semver from 'semver'; import lt from 'semver/functions/lt';
import coerce from 'semver/functions/coerce';
import database from '../../lib/database'; import database from '../../lib/database';
import { deleteRoom as deleteRoomAction } from '../../actions/room'; import { deleteRoom as deleteRoomAction } from '../../actions/room';
@ -407,7 +409,7 @@ class RoomInfoEditView extends React.Component {
isServerVersionLowerThan = (version) => { isServerVersionLowerThan = (version) => {
const { serverVersion } = this.props; const { serverVersion } = this.props;
return serverVersion && semver.lt(semver.coerce(serverVersion), version); return serverVersion && lt(coerce(serverVersion), version);
} }
render() { render() {
@ -547,7 +549,7 @@ class RoomInfoEditView extends React.Component {
] ]
: null : null
} }
{serverVersion && !semver.lt(serverVersion, '3.0.0') ? ( {serverVersion && !lt(serverVersion, '3.0.0') ? (
<SwitchContainer <SwitchContainer
value={enableSysMes} value={enableSysMes}
leftLabelPrimary={I18n.t('Hide_System_Messages')} leftLabelPrimary={I18n.t('Hide_System_Messages')}

View File

@ -4,7 +4,7 @@ import { View, Text, ScrollView } from 'react-native';
import { BorderlessButton } from 'react-native-gesture-handler'; import { BorderlessButton } from 'react-native-gesture-handler';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import UAParser from 'ua-parser-js'; import UAParser from 'ua-parser-js';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import database from '../../lib/database'; import database from '../../lib/database';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
@ -169,7 +169,7 @@ class RoomInfoView extends React.Component {
loadUser = async() => { loadUser = async() => {
const { room, roomUser } = this.state; const { room, roomUser } = this.state;
if (_.isEmpty(roomUser)) { if (isEmpty(roomUser)) {
try { try {
const roomUserId = RocketChat.getUidDirectMessage(room); const roomUserId = RocketChat.getUidDirectMessage(room);
const result = await RocketChat.getUserInfo(roomUserId); const result = await RocketChat.getUserInfo(roomUserId);
@ -224,7 +224,7 @@ class RoomInfoView extends React.Component {
// We don't need to create a direct // We don't need to create a direct
const member = route.params?.member; const member = route.params?.member;
if (!_.isEmpty(member)) { if (!isEmpty(member)) {
return resolve(); return resolve();
} }

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
import useDeepCompareEffect from 'use-deep-compare-effect'; import useDeepCompareEffect from 'use-deep-compare-effect';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import { withTheme } from '../theme'; import { withTheme } from '../theme';
@ -16,7 +16,7 @@ const ScreenLockedView = ({ theme }) => {
const [data, setData] = useState({}); const [data, setData] = useState({});
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
if (!_.isEmpty(data)) { if (!isEmpty(data)) {
setVisible(true); setVisible(true);
} else { } else {
setVisible(false); setVisible(false);

View File

@ -1,33 +1,20 @@
import React from 'react'; import React from 'react';
import { import { FlatList } from 'react-native';
FlatList, StyleSheet, View
} from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
import I18n from '../i18n'; import I18n from '../i18n';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import ServerItem, { ROW_HEIGHT } from '../presentation/ServerItem'; import ServerItem, { ROW_HEIGHT } from '../presentation/ServerItem';
import sharedStyles from './Styles';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { withTheme } from '../theme'; import database from '../lib/database';
import SafeAreaView from '../containers/SafeAreaView'; import SafeAreaView from '../containers/SafeAreaView';
import * as List from '../containers/List';
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.id; const keyExtractor = item => item.id;
const styles = StyleSheet.create({
list: {
marginVertical: 32,
...sharedStyles.separatorVertical
},
separator: {
...sharedStyles.separatorBottom,
marginLeft: 48
}
});
class SelectServerView extends React.Component { class SelectServerView extends React.Component {
static navigationOptions = () => ({ static navigationOptions = () => ({
title: I18n.t('Select_Server') title: I18n.t('Select_Server')
@ -35,19 +22,16 @@ class SelectServerView extends React.Component {
static propTypes = { static propTypes = {
server: PropTypes.string, server: PropTypes.string,
route: PropTypes.object, navigation: PropTypes.object
navigation: PropTypes.object,
theme: PropTypes.string
} }
constructor(props) { state = { servers: [] };
super(props);
const { route } = this.props; async componentDidMount() {
const servers = route.params?.servers ?? []; const serversDB = database.servers;
const filteredServers = servers.filter(server => server.roomsUpdatedAt); const serversCollection = serversDB.collections.get('servers');
this.state = { const servers = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetch();
servers: filteredServers this.setState({ servers });
};
} }
select = async(server) => { select = async(server) => {
@ -62,43 +46,34 @@ class SelectServerView extends React.Component {
} }
renderItem = ({ item }) => { renderItem = ({ item }) => {
const { server, theme } = this.props; const { server } = this.props;
return ( return (
<ServerItem <ServerItem
onPress={() => this.select(item.id)} onPress={() => this.select(item.id)}
item={item} item={item}
hasCheck={item.id === server} hasCheck={item.id === server}
theme={theme}
/> />
); );
} }
renderSeparator = () => {
const { theme } = this.props;
return <View style={[styles.separator, { borderColor: themes[theme].separatorColor }]} />;
}
render() { render() {
const { servers } = this.state; const { servers } = this.state;
const { theme } = this.props;
return ( return (
<SafeAreaView> <SafeAreaView>
<StatusBar /> <StatusBar />
<View style={[styles.list, { borderColor: themes[theme].separatorColor }]}>
<FlatList <FlatList
data={servers} data={servers}
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
renderItem={this.renderItem} renderItem={this.renderItem}
getItemLayout={getItemLayout} getItemLayout={getItemLayout} // Refactor row_height
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }} contentContainerStyle={List.styles.contentContainerStyleFlatList}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
ListHeaderComponent={List.Separator}
ListFooterComponent={List.Separator}
enableEmptySections enableEmptySections
removeClippedSubviews removeClippedSubviews
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
windowSize={7}
bounces={false}
/> />
</View>
</SafeAreaView> </SafeAreaView>
); );
} }
@ -108,4 +83,4 @@ const mapStateToProps = (({ share }) => ({
server: share.server.server server: share.server.server
})); }));
export default connect(mapStateToProps)(withTheme(SelectServerView)); export default connect(mapStateToProps)(SelectServerView);

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { View, StyleSheet, FlatList } from 'react-native'; import { View, StyleSheet, FlatList } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import equal from 'deep-equal';
import { orderBy } from 'lodash'; import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import database from '../lib/database'; import database from '../lib/database';

View File

@ -18,9 +18,8 @@ import ServerItem from '../../presentation/ServerItem';
import * as HeaderButton from '../../containers/HeaderButton'; import * as HeaderButton from '../../containers/HeaderButton';
import ShareListHeader from './Header'; import ShareListHeader from './Header';
import ActivityIndicator from '../../containers/ActivityIndicator'; import ActivityIndicator from '../../containers/ActivityIndicator';
import * as List from '../../containers/List';
import styles from './styles'; import styles from './styles';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { animateNextTransition } from '../../utils/layoutAnimation'; import { animateNextTransition } from '../../utils/layoutAnimation';
import { withTheme } from '../../theme'; import { withTheme } from '../../theme';
@ -33,7 +32,7 @@ const permission = {
message: I18n.t('Read_External_Permission_Message') message: I18n.t('Read_External_Permission_Message')
}; };
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const getItemLayout = (data, index) => ({ length: data.length, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.rid; const keyExtractor = item => item.rid;
class ShareListView extends React.Component { class ShareListView extends React.Component {
@ -47,13 +46,12 @@ class ShareListView extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.chats = [];
this.state = { this.state = {
searching: false, searching: false,
searchText: '', searchText: '',
searchResults: [], searchResults: [],
chats: [], chats: [],
servers: [], serversCount: 0,
attachments: [], attachments: [],
text: '', text: '',
loading: true, loading: true,
@ -61,9 +59,11 @@ class ShareListView extends React.Component {
needsPermission: isAndroid || false needsPermission: isAndroid || false
}; };
this.setHeader(); this.setHeader();
if (isAndroid) {
this.unsubscribeFocus = props.navigation.addListener('focus', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)); this.unsubscribeFocus = props.navigation.addListener('focus', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress));
this.unsubscribeBlur = props.navigation.addListener('blur', () => BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress)); this.unsubscribeBlur = props.navigation.addListener('blur', () => BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress));
} }
}
async componentDidMount() { async componentDidMount() {
const { server } = this.props; const { server } = this.props;
@ -108,21 +108,20 @@ class ShareListView extends React.Component {
return true; return true;
} }
const { server, theme, userId } = this.props; const { server, userId } = this.props;
if (server !== nextProps.server) { if (server !== nextProps.server) {
return true; return true;
} }
if (userId !== nextProps.userId) { if (userId !== nextProps.userId) {
return true; return true;
} }
if (theme !== nextProps.theme) {
return true;
}
const { searchResults } = this.state; const { searchResults } = this.state;
if (nextState.searching) {
if (!isEqual(nextState.searchResults, searchResults)) { if (!isEqual(nextState.searchResults, searchResults)) {
return true; return true;
} }
}
return false; return false;
} }
@ -189,7 +188,7 @@ class ShareListView extends React.Component {
this.setState(...args); this.setState(...args);
} }
query = (text) => { query = async(text) => {
const db = database.active; const db = database.active;
const defaultWhereClause = [ const defaultWhereClause = [
Q.where('archived', false), Q.where('archived', false),
@ -200,26 +199,35 @@ class ShareListView extends React.Component {
]; ];
if (text) { if (text) {
const likeString = sanitizeLikeString(text); const likeString = sanitizeLikeString(text);
return db.collections defaultWhereClause.push(
.get('subscriptions')
.query(
...defaultWhereClause,
Q.or( Q.or(
Q.where('name', Q.like(`%${ likeString }%`)), Q.where('name', Q.like(`%${ likeString }%`)),
Q.where('fname', Q.like(`%${ likeString }%`)) Q.where('fname', Q.like(`%${ likeString }%`))
) )
).fetch(); );
} }
return db.collections.get('subscriptions').query(...defaultWhereClause).fetch(); const data = await db.collections.get('subscriptions').query(...defaultWhereClause).fetch();
return data.map(item => ({
rid: item.rid,
t: item.t,
name: item.name,
fname: item.fname,
blocked: item.blocked,
blocker: item.blocker,
prid: item.prid,
uids: item.uids,
usernames: item.usernames,
topic: item.topic
}));
} }
getSubscriptions = async(server) => { getSubscriptions = async(server) => {
const serversDB = database.servers; const serversDB = database.servers;
if (server) { if (server) {
this.chats = await this.query(); const chats = await this.query();
const serversCollection = serversDB.collections.get('servers'); const serversCollection = serversDB.collections.get('servers');
this.servers = await serversCollection.query().fetch(); const serversCount = await serversCollection.query(Q.where('rooms_updated_at', Q.notEq(null))).fetchCount();
let serverInfo = {}; let serverInfo = {};
try { try {
serverInfo = await serversCollection.find(server); serverInfo = await serversCollection.find(server);
@ -228,8 +236,8 @@ class ShareListView extends React.Component {
} }
this.internalSetState({ this.internalSetState({
chats: this.chats ?? [], chats: chats ?? [],
servers: this.servers ?? [], serversCount,
loading: false, loading: false,
serverInfo serverInfo
}); });
@ -306,11 +314,14 @@ class ShareListView extends React.Component {
} }
return ( return (
<>
<View style={[styles.headerContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}> <View style={[styles.headerContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}>
<Text style={[styles.headerText, { color: themes[theme].titleText }]}> <Text style={[styles.headerText, { color: themes[theme].titleText }]}>
{I18n.t(header)} {I18n.t(header)}
</Text> </Text>
</View> </View>
<List.Separator />
</>
); );
} }
@ -353,41 +364,19 @@ class ShareListView extends React.Component {
); );
} }
renderSeparator = () => {
const { theme } = this.props;
return <View style={[styles.separator, { borderColor: themes[theme].separatorColor }]} />;
}
renderBorderBottom = () => {
const { theme } = this.props;
return <View style={[styles.borderBottom, { borderColor: themes[theme].separatorColor }]} />;
}
renderSelectServer = () => { renderSelectServer = () => {
const { servers } = this.state; const { serverInfo } = this.state;
const { server, theme, navigation } = this.props; const { navigation } = this.props;
const currentServer = servers.find(serverFiltered => serverFiltered.id === server); return (
return currentServer ? (
<> <>
{this.renderSectionHeader('Select_Server')} {this.renderSectionHeader('Select_Server')}
<View
style={[
styles.bordered,
{
borderColor: themes[theme].separatorColor,
backgroundColor: themes[theme].auxiliaryBackground
}
]}
>
<ServerItem <ServerItem
server={server} onPress={() => navigation.navigate('SelectServerView')}
onPress={() => navigation.navigate('SelectServerView', { servers: this.servers })} item={serverInfo}
item={currentServer}
theme={theme}
/> />
</View> <List.Separator />
</> </>
) : null; );
} }
renderEmptyComponent = () => { renderEmptyComponent = () => {
@ -400,23 +389,25 @@ class ShareListView extends React.Component {
} }
renderHeader = () => { renderHeader = () => {
const { searching } = this.state; const { searching, serversCount } = this.state;
if (searching) {
return null;
}
if (serversCount === 1) {
return this.renderSectionHeader('Chats');
}
return ( return (
<>
{ !searching
? (
<> <>
{this.renderSelectServer()} {this.renderSelectServer()}
{this.renderSectionHeader('Chats')} {this.renderSectionHeader('Chats')}
</> </>
)
: null
}
</>
); );
} }
renderContent = () => { render = () => {
const { const {
chats, loading, searchResults, searching, searchText, needsPermission chats, loading, searchResults, searching, searchText, needsPermission
} = this.state; } = this.state;
@ -428,17 +419,20 @@ class ShareListView extends React.Component {
if (needsPermission) { if (needsPermission) {
return ( return (
<SafeAreaView>
<ScrollView <ScrollView
style={{ backgroundColor: themes[theme].auxiliaryBackground }} style={{ backgroundColor: themes[theme].backgroundColor }}
contentContainerStyle={[styles.container, styles.centered, { backgroundColor: themes[theme].backgroundColor }]} contentContainerStyle={[styles.container, styles.centered, { backgroundColor: themes[theme].backgroundColor }]}
> >
<Text style={[styles.permissionTitle, { color: themes[theme].titleText }]}>{permission.title}</Text> <Text style={[styles.permissionTitle, { color: themes[theme].titleText }]}>{permission.title}</Text>
<Text style={[styles.permissionMessage, { color: themes[theme].bodyText }]}>{permission.message}</Text> <Text style={[styles.permissionMessage, { color: themes[theme].bodyText }]}>{permission.message}</Text>
</ScrollView> </ScrollView>
</SafeAreaView>
); );
} }
return ( return (
<SafeAreaView>
<FlatList <FlatList
data={searching ? searchResults : chats} data={searching ? searchResults : chats}
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
@ -446,23 +440,13 @@ class ShareListView extends React.Component {
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }} contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
renderItem={this.renderItem} renderItem={this.renderItem}
getItemLayout={getItemLayout} getItemLayout={getItemLayout}
ItemSeparatorComponent={this.renderSeparator} ItemSeparatorComponent={List.Separator}
// ListHeaderComponent={this.renderHeader} // ListHeaderComponent={this.renderHeader}
ListFooterComponent={!searching && this.renderBorderBottom} ListFooterComponent={!searching || searchResults.length > 0 ? <List.Separator /> : null}
ListHeaderComponentStyle={!searching ? { ...styles.borderBottom, borderColor: themes[theme].separatorColor } : {}}
ListEmptyComponent={searching && searchText ? this.renderEmptyComponent : null} ListEmptyComponent={searching && searchText ? this.renderEmptyComponent : null}
removeClippedSubviews removeClippedSubviews
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
initialNumToRender={12}
/> />
);
}
render() {
return (
<SafeAreaView>
<StatusBar />
{this.renderContent()}
</SafeAreaView> </SafeAreaView>
); );
} }

View File

@ -14,6 +14,7 @@ import { THUMBS_HEIGHT } from './constants';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import { allowPreview } from './utils'; import { allowPreview } from './utils';
import I18n from '../../i18n'; import I18n from '../../i18n';
import { isAndroid } from '../../utils/deviceInfo';
const MESSAGEBOX_HEIGHT = 56; const MESSAGEBOX_HEIGHT = 56;
@ -63,7 +64,8 @@ const Preview = React.memo(({
const calculatedHeight = height - insets.top - insets.bottom - MESSAGEBOX_HEIGHT - thumbsHeight - headerHeight; const calculatedHeight = height - insets.top - insets.bottom - MESSAGEBOX_HEIGHT - thumbsHeight - headerHeight;
if (item?.canUpload) { if (item?.canUpload) {
if (type?.match(/video/)) { // Disable video preview on iOS to save memory
if (isAndroid && type?.match(/video/)) {
return ( return (
<ScrollView style={{ height: calculatedHeight }}> <ScrollView style={{ height: calculatedHeight }}>
<Video <Video
@ -81,8 +83,8 @@ const Preview = React.memo(({
} }
// Disallow preview of images too big in order to prevent memory issues on iOS share extension // Disallow preview of images too big in order to prevent memory issues on iOS share extension
if (allowPreview(isShareExtension, item?.size)) {
if (type?.match(/image/)) { if (type?.match(/image/)) {
if (allowPreview(isShareExtension, item?.size)) {
return ( return (
<ImageViewer <ImageViewer
uri={item.path} uri={item.path}

View File

@ -90,6 +90,17 @@ const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
} }
if (type?.match(/video/)) { if (type?.match(/video/)) {
if (isIOS) {
return (
<View style={[styles.thumb, { borderColor: themes[theme].borderColor }]}>
<CustomIcon
name='camera'
size={30}
color={themes[theme].tintColor}
/>
</View>
);
} else {
const { uri } = item; const { uri } = item;
return ( return (
<> <>
@ -103,6 +114,7 @@ const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
</> </>
); );
} }
}
// Multiple files upload of files different than image/video is not implemented, so there's no thumb // Multiple files upload of files different than image/video is not implemented, so there's no thumb
return null; return null;

View File

@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import { View, Text, NativeModules } from 'react-native'; import { View, Text, NativeModules } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ShareExtension from 'rn-extensions-share'; import ShareExtension from 'rn-extensions-share';
import * as VideoThumbnails from 'expo-video-thumbnails';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -24,6 +23,7 @@ import { getUserSelector } from '../../selectors/login';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import database from '../../lib/database'; import database from '../../lib/database';
import { canUploadFile } from '../../utils/media'; import { canUploadFile } from '../../utils/media';
import { isAndroid } from '../../utils/deviceInfo';
class ShareView extends Component { class ShareView extends Component {
constructor(props) { constructor(props) {
@ -119,8 +119,9 @@ class ShareView extends Component {
item.error = error; item.error = error;
// get video thumbnails // get video thumbnails
if (item.mime?.match?.(/video/)) { if (isAndroid && this.files.length > 1 && item.mime?.match?.(/video/)) {
try { try {
const VideoThumbnails = require('expo-video-thumbnails');
const { uri } = await VideoThumbnails.getThumbnailAsync(item.path); const { uri } = await VideoThumbnails.getThumbnailAsync(item.path);
item.uri = uri; item.uri = uri;
} catch { } catch {

View File

@ -21,7 +21,41 @@ abstract_target 'defaults' do
end end
target 'ShareRocketChatRN' do target 'ShareRocketChatRN' do
all_pods pod 'EXAppleAuthentication', :path=> "../node_modules/expo-apple-authentication/ios"
pod 'EXAV', :path=> "../node_modules/expo-av/ios"
pod 'EXFileSystem', :path=> "../node_modules/expo-file-system/ios"
pod 'EXHaptics', :path=> "../node_modules/expo-haptics/ios"
pod 'EXImageLoader', :path=> "../node_modules/expo-image-loader/ios"
pod 'UMCore', :path=> "../node_modules/@unimodules/core/ios"
pod 'UMImageLoaderInterface', :path=> "../node_modules/unimodules-image-loader-interface/ios"
pod 'UMReactNativeAdapter', :path=> "../node_modules/@unimodules/react-native-adapter/ios"
use_react_native!(:path => '../node_modules/react-native')
pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app'
pod 'RNFBAnalytics', :path => '../node_modules/@react-native-firebase/analytics'
pod 'RNFBCrashlytics', :path => '../node_modules/@react-native-firebase/crashlytics'
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
pod 'RNCMaskedView', :path => '../node_modules/@react-native-community/masked-view'
pod 'RNFastImage', :path => '../node_modules/@rocket.chat/react-native-fast-image'
pod 'BugsnagReactNative', :path => '../node_modules/bugsnag-react-native'
pod 'react-native-appearance', :path => '../node_modules/react-native-appearance'
pod 'RNConfigReader', :path => '../node_modules/react-native-config-reader'
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
pod 'react-native-document-picker', :path => '../node_modules/react-native-document-picker'
pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
pod 'RNLocalize', :path => '../node_modules/react-native-localize'
pod 'react-native-mmkv-storage', :path => '../node_modules/react-native-mmkv-storage'
pod 'RNReanimated', :path => '../node_modules/react-native-reanimated'
pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
pod 'RNScreens', :path => '../node_modules/react-native-screens'
pod 'react-native-simple-crypto', :path => '../node_modules/react-native-simple-crypto'
pod 'ReactNativeUiLib', :path => '../node_modules/react-native-ui-lib/lib'
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
pod 'rn-extensions-share', :path => '../node_modules/rn-extensions-share'
pod 'rn-fetch-blob', :path => '../node_modules/rn-fetch-blob'
pod 'RNRootView', :path => '../node_modules/rn-root-view'
pod 'react-native-orientation-locker', :path => '../node_modules/react-native-orientation-locker'
end end
# used to get user credentials # used to get user credentials

View File

@ -34,7 +34,7 @@ PODS:
- EXPermissions (9.0.1): - EXPermissions (9.0.1):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- EXVideoThumbnails (4.2.1): - EXVideoThumbnails (5.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXWebBrowser (8.3.1): - EXWebBrowser (8.3.1):
@ -895,7 +895,7 @@ SPEC CHECKSUMS:
EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812 EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812
EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8 EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8
EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8 EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
EXVideoThumbnails: f70bdc5511749f3181028f5000bcb7be203c631d EXVideoThumbnails: 0be939563a5d46ce0d1fa9baea7d454b99475763
EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4 EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
@ -1003,6 +1003,6 @@ SPEC CHECKSUMS:
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6 Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 8477bd3277dec7b3190105d922512e3791bc026f PODFILE CHECKSUM: 0351d0973911a397f1cb4e45f0b0f590b14816e3
COCOAPODS: 1.9.3 COCOAPODS: 1.9.3

View File

@ -1,20 +1,17 @@
{ {
"name": "EXVideoThumbnails", "name": "EXVideoThumbnails",
"version": "4.2.1", "version": "5.0.0",
"summary": "ExpoVideoThumbnails standalone module", "summary": "ExpoVideoThumbnails standalone module",
"description": "ExpoVideoThumbnails standalone module", "description": "ExpoVideoThumbnails standalone module",
"license": "MIT", "license": "MIT",
"authors": "650 Industries, Inc.", "authors": "650 Industries, Inc.",
"homepage": "https://github.com/expo/expo/tree/master/packages/expo-video-thumbnails", "homepage": "https://docs.expo.io/versions/latest/sdk/video-thumbnails/",
"platforms": { "platforms": {
"ios": "10.0" "ios": "11.0"
}, },
"source": { "source": {
"git": "https://github.com/expo/expo.git" "git": "https://github.com/expo/expo.git"
}, },
"source_files": "EXVideoThumbnails/**/*.{h,m}",
"preserve_paths": "EXVideoThumbnails/**/*.{h,m}",
"requires_arc": true,
"dependencies": { "dependencies": {
"UMCore": [ "UMCore": [
@ -22,5 +19,6 @@
"UMFileSystemInterface": [ "UMFileSystemInterface": [
] ]
} },
"source_files": "EXVideoThumbnails/**/*.{h,m}"
} }

View File

@ -34,7 +34,7 @@ PODS:
- EXPermissions (9.0.1): - EXPermissions (9.0.1):
- UMCore - UMCore
- UMPermissionsInterface - UMPermissionsInterface
- EXVideoThumbnails (4.2.1): - EXVideoThumbnails (5.0.0):
- UMCore - UMCore
- UMFileSystemInterface - UMFileSystemInterface
- EXWebBrowser (8.3.1): - EXWebBrowser (8.3.1):
@ -895,7 +895,7 @@ SPEC CHECKSUMS:
EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812 EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812
EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8 EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8
EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8 EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
EXVideoThumbnails: f70bdc5511749f3181028f5000bcb7be203c631d EXVideoThumbnails: 0be939563a5d46ce0d1fa9baea7d454b99475763
EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4 EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
@ -1003,6 +1003,6 @@ SPEC CHECKSUMS:
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6 Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 8477bd3277dec7b3190105d922512e3791bc026f PODFILE CHECKSUM: 0351d0973911a397f1cb4e45f0b0f590b14816e3
COCOAPODS: 1.9.3 COCOAPODS: 1.9.3

File diff suppressed because it is too large Load Diff

View File

@ -97,7 +97,6 @@ EOM
esac esac
} }
if [[ "$CONFIGURATION" == "Debug" ]]; then if [[ "$CONFIGURATION" == "Debug" ]]; then
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"
@ -115,10 +114,8 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle" install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle"
fi fi
if [[ "$CONFIGURATION" == "Release" ]]; then if [[ "$CONFIGURATION" == "Release" ]]; then
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"
@ -136,7 +133,6 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"
install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf" install_resource "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle" install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle"
fi fi
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -23,7 +23,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>4.14.0</string> <string>4.14.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>

View File

@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>XPC!</string> <string>XPC!</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>4.14.0</string> <string>4.14.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>KeychainGroup</key> <key>KeychainGroup</key>

View File

@ -59,7 +59,7 @@
"expo-haptics": "8.2.1", "expo-haptics": "8.2.1",
"expo-keep-awake": "8.2.1", "expo-keep-awake": "8.2.1",
"expo-local-authentication": "9.2.0", "expo-local-authentication": "9.2.0",
"expo-video-thumbnails": "4.2.1", "expo-video-thumbnails": "^5.0.0",
"expo-web-browser": "8.3.1", "expo-web-browser": "8.3.1",
"hoist-non-react-statics": "3.3.2", "hoist-non-react-statics": "3.3.2",
"i18n-js": "3.7.1", "i18n-js": "3.7.1",

View File

@ -6849,10 +6849,10 @@ expo-permissions@~9.0.1:
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-9.0.1.tgz#dc10b58654bbe39bbbed5827369942b01b08055e" resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-9.0.1.tgz#dc10b58654bbe39bbbed5827369942b01b08055e"
integrity sha512-CosJgy8XQRN/OFG2JTQDcFxz3XTGi27coCMym/hVXWtQfk0z6PwdRG5IXHfLGuSckwIcgmirrwm2+Zc0X3MmNg== integrity sha512-CosJgy8XQRN/OFG2JTQDcFxz3XTGi27coCMym/hVXWtQfk0z6PwdRG5IXHfLGuSckwIcgmirrwm2+Zc0X3MmNg==
expo-video-thumbnails@4.2.1: expo-video-thumbnails@^5.0.0:
version "4.2.1" version "5.0.0"
resolved "https://registry.yarnpkg.com/expo-video-thumbnails/-/expo-video-thumbnails-4.2.1.tgz#8867ecf143acbca6425da05d97beaa0cd31b2bc2" resolved "https://registry.yarnpkg.com/expo-video-thumbnails/-/expo-video-thumbnails-5.0.0.tgz#819adcf2e79d774a4f0d70da200bf053e07cd059"
integrity sha512-HxnK/AzGHXNWCB0GnTLXDnk76Nv/MgId1XgrxuESsPFkzUptxhRsMadD/27Diny37Ele2MZVAjID25ZMbs8N5A== integrity sha512-jYmpuKzGn4i4O52TkLiVm4/06VrGyJWXSuU99zAdN5N//8J0aA8MzC3nxk+PKQcHGj36enOPT5P5aHARFbtVww==
expo-web-browser@8.3.1: expo-web-browser@8.3.1:
version "8.3.1" version "8.3.1"
@ -13189,7 +13189,7 @@ react-native-windows@^0.62.0-0:
uuid "^3.3.2" uuid "^3.3.2"
xml-parser "^1.2.1" xml-parser "^1.2.1"
react-native@RocketChat/react-native#0.63.4: react-native@RocketChat/react-native#0.63.4, react-native@^0.63.1:
version "0.63.4" version "0.63.4"
resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/616299bbc01eeb56cb7d5b52e543051e1ad2d136" resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/616299bbc01eeb56cb7d5b52e543051e1ad2d136"
dependencies: dependencies:
@ -13832,6 +13832,8 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
rn-extensions-share@RocketChat/rn-extensions-share: rn-extensions-share@RocketChat/rn-extensions-share:
version "2.4.1" version "2.4.1"
resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c" resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c"
dependencies:
react-native "^0.63.1"
rn-fetch-blob@0.12.0: rn-fetch-blob@0.12.0:
version "0.12.0" version "0.12.0"