[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
This commit is contained in:
Diego Mello 2021-02-01 14:18:55 -03:00 committed by GitHub
parent cf59644a56
commit acd5f04314
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1569 additions and 4729 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import { View, Text, InteractionManager } from 'react-native';
import _ from 'lodash';
import isEmpty from 'lodash/isEmpty';
import PropTypes from 'prop-types';
import { sha256 } from 'js-sha256';
import Modal from 'react-native-modal';
@ -47,7 +47,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
const sendEmail = () => RocketChat.sendEmailCode();
useDeepCompareEffect(() => {
if (!_.isEmpty(data)) {
if (!isEmpty(data)) {
setCode('');
setVisible(true);
} 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 database from '../database';
@ -33,7 +34,7 @@ export function getEnterpriseModules() {
return new Promise(async(resolve) => {
try {
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
const enterpriseModules = await this.methodCallWrapper('license:getModules');
if (enterpriseModules) {

View File

@ -1,5 +1,5 @@
import { InteractionManager } from 'react-native';
import semver from 'semver';
import lt from 'semver/functions/lt';
import orderBy from 'lodash/orderBy';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -91,7 +91,7 @@ export function getCustomEmojis() {
const updatedSince = await getUpdatedSince(allRecords);
// 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
const result = await this.sdk.get('emoji-custom');

View File

@ -1,7 +1,7 @@
import { InteractionManager } from 'react-native';
import semver from 'semver';
import lt from 'semver/functions/lt';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { orderBy } from 'lodash';
import orderBy from 'lodash/orderBy';
import database from '../database';
import log from '../../utils/log';
@ -79,7 +79,7 @@ export default function() {
const allRecords = await permissionsCollection.query().fetch();
// 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
const result = await this.sdk.get('permissions.list');
if (!result.success) {

View File

@ -1,5 +1,6 @@
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 reduxStore from '../createStore';
@ -11,7 +12,7 @@ export function subscribeUsersPresence() {
const serverVersion = reduxStore.getState().server.version;
// 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) {
clearTimeout(this.activeUsersSubTimeout);
this.activeUsersSubTimeout = false;
@ -36,11 +37,11 @@ export default async function getUsersPresence() {
const { user: loggedUser } = reduxStore.getState().login;
// 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 = {};
// 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 (!ids.length) {
return;

View File

@ -1,5 +1,7 @@
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 {
Rocketchat as RocketchatClient,
settings as RocketChatSettings
@ -132,7 +134,7 @@ const RocketChat = {
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 {
success: false,
message: I18n.t('Invalid_server_version', {
@ -465,7 +467,7 @@ const RocketChat = {
// Force normalized params for 2FA starting RC 3.9.0.
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 password = params.password ?? params.ldapPass ?? params.crowdPassword;
params = { user, password };
@ -1372,7 +1374,7 @@ const RocketChat = {
},
readThreads(tmid) {
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
return this.methodCallWrapper('readThreads', tmid);
}

View File

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

View File

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

View File

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

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 }`;
@ -8,14 +9,12 @@ export const avatarURL = ({
let room;
if (type === 'd') {
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 }`;
} else {
room = `@${ text }`;
}
const uriSize = size > 100 ? size : 100;
const { id, token } = user;
let query = '';
if (id && token && blockUnauthenticatedAccess) {
@ -30,8 +29,8 @@ export const avatarURL = ({
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 Orientation from 'react-native-orientation-locker';
import useDeepCompareEffect from 'use-deep-compare-effect';
import _ from 'lodash';
import isEmpty from 'lodash/isEmpty';
import Modal from 'react-native-modal';
import Touchable from 'react-native-platform-touchable';
@ -32,7 +32,7 @@ const ChangePasscodeView = React.memo(({ theme }) => {
const [data, setData] = useState({});
useDeepCompareEffect(() => {
if (!_.isEmpty(data)) {
if (!isEmpty(data)) {
setVisible(true);
} else {
setVisible(false);

View File

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

View File

@ -6,7 +6,8 @@ import prompt from 'react-native-prompt-android';
import SHA256 from 'js-sha256';
import ImagePicker from 'react-native-image-crop-picker';
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 KeyboardView from '../../presentation/KeyboardView';

View File

@ -4,8 +4,9 @@ import {
View, Text, Alert, Share, Switch
} from 'react-native';
import { connect } from 'react-redux';
import _ from 'lodash';
import semver from 'semver';
import isEmpty from 'lodash/isEmpty';
import lt from 'semver/functions/lt';
import coerce from 'semver/functions/coerce';
import Touch from '../../utils/touch';
import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
@ -113,7 +114,7 @@ class RoomActionsView extends React.Component {
} catch (e) {
log(e);
}
} else if (room.t === 'd' && _.isEmpty(member)) {
} else if (room.t === 'd' && isEmpty(member)) {
this.updateRoomMember();
}
@ -251,7 +252,7 @@ class RoomActionsView extends React.Component {
const { encrypted } = room;
const { serverVersion } = this.props;
let hasPermission = false;
if (serverVersion && semver.lt(semver.coerce(serverVersion), '3.11.0')) {
if (serverVersion && lt(coerce(serverVersion), '3.11.0')) {
hasPermission = canEdit;
} else {
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 isEqual from 'lodash/isEqual';
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 { deleteRoom as deleteRoomAction } from '../../actions/room';
@ -407,7 +409,7 @@ class RoomInfoEditView extends React.Component {
isServerVersionLowerThan = (version) => {
const { serverVersion } = this.props;
return serverVersion && semver.lt(semver.coerce(serverVersion), version);
return serverVersion && lt(coerce(serverVersion), version);
}
render() {
@ -547,7 +549,7 @@ class RoomInfoEditView extends React.Component {
]
: null
}
{serverVersion && !semver.lt(serverVersion, '3.0.0') ? (
{serverVersion && !lt(serverVersion, '3.0.0') ? (
<SwitchContainer
value={enableSysMes}
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 { connect } from 'react-redux';
import UAParser from 'ua-parser-js';
import _ from 'lodash';
import isEmpty from 'lodash/isEmpty';
import database from '../../lib/database';
import { CustomIcon } from '../../lib/Icons';
@ -169,7 +169,7 @@ class RoomInfoView extends React.Component {
loadUser = async() => {
const { room, roomUser } = this.state;
if (_.isEmpty(roomUser)) {
if (isEmpty(roomUser)) {
try {
const roomUserId = RocketChat.getUidDirectMessage(room);
const result = await RocketChat.getUserInfo(roomUserId);
@ -224,7 +224,7 @@ class RoomInfoView extends React.Component {
// We don't need to create a direct
const member = route.params?.member;
if (!_.isEmpty(member)) {
if (!isEmpty(member)) {
return resolve();
}

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ import { THUMBS_HEIGHT } from './constants';
import sharedStyles from '../Styles';
import { allowPreview } from './utils';
import I18n from '../../i18n';
import { isAndroid } from '../../utils/deviceInfo';
const MESSAGEBOX_HEIGHT = 56;
@ -63,7 +64,8 @@ const Preview = React.memo(({
const calculatedHeight = height - insets.top - insets.bottom - MESSAGEBOX_HEIGHT - thumbsHeight - headerHeight;
if (item?.canUpload) {
if (type?.match(/video/)) {
// Disable video preview on iOS to save memory
if (isAndroid && type?.match(/video/)) {
return (
<ScrollView style={{ height: calculatedHeight }}>
<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
if (allowPreview(isShareExtension, item?.size)) {
if (type?.match(/image/)) {
if (type?.match(/image/)) {
if (allowPreview(isShareExtension, item?.size)) {
return (
<ImageViewer
uri={item.path}

View File

@ -90,18 +90,30 @@ const ThumbContent = React.memo(({ item, theme, isShareExtension }) => {
}
if (type?.match(/video/)) {
const { uri } = item;
return (
<>
<Image source={{ uri }} style={styles.thumb} />
<CustomIcon
name='camera-filled'
size={20}
color={themes[theme].buttonText}
style={styles.videoThumbIcon}
/>
</>
);
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;
return (
<>
<Image source={{ uri }} style={styles.thumb} />
<CustomIcon
name='camera-filled'
size={20}
color={themes[theme].buttonText}
style={styles.videoThumbIcon}
/>
</>
);
}
}
// Multiple files upload of files different than image/video is not implemented, so there's no thumb

View File

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

View File

@ -21,7 +21,41 @@ abstract_target 'defaults' do
end
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
# used to get user credentials

View File

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

View File

@ -1,20 +1,17 @@
{
"name": "EXVideoThumbnails",
"version": "4.2.1",
"version": "5.0.0",
"summary": "ExpoVideoThumbnails standalone module",
"description": "ExpoVideoThumbnails standalone module",
"license": "MIT",
"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": {
"ios": "10.0"
"ios": "11.0"
},
"source": {
"git": "https://github.com/expo/expo.git"
},
"source_files": "EXVideoThumbnails/**/*.{h,m}",
"preserve_paths": "EXVideoThumbnails/**/*.{h,m}",
"requires_arc": true,
"dependencies": {
"UMCore": [
@ -22,5 +19,6 @@
"UMFileSystemInterface": [
]
}
},
"source_files": "EXVideoThumbnails/**/*.{h,m}"
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -97,7 +97,6 @@ EOM
esac
}
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/Entypo.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/Zocial.ttf"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle"
fi
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/Entypo.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/Zocial.ttf"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle"
install_resource "${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle"
fi
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

@ -59,7 +59,7 @@
"expo-haptics": "8.2.1",
"expo-keep-awake": "8.2.1",
"expo-local-authentication": "9.2.0",
"expo-video-thumbnails": "4.2.1",
"expo-video-thumbnails": "^5.0.0",
"expo-web-browser": "8.3.1",
"hoist-non-react-statics": "3.3.2",
"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"
integrity sha512-CosJgy8XQRN/OFG2JTQDcFxz3XTGi27coCMym/hVXWtQfk0z6PwdRG5IXHfLGuSckwIcgmirrwm2+Zc0X3MmNg==
expo-video-thumbnails@4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/expo-video-thumbnails/-/expo-video-thumbnails-4.2.1.tgz#8867ecf143acbca6425da05d97beaa0cd31b2bc2"
integrity sha512-HxnK/AzGHXNWCB0GnTLXDnk76Nv/MgId1XgrxuESsPFkzUptxhRsMadD/27Diny37Ele2MZVAjID25ZMbs8N5A==
expo-video-thumbnails@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/expo-video-thumbnails/-/expo-video-thumbnails-5.0.0.tgz#819adcf2e79d774a4f0d70da200bf053e07cd059"
integrity sha512-jYmpuKzGn4i4O52TkLiVm4/06VrGyJWXSuU99zAdN5N//8J0aA8MzC3nxk+PKQcHGj36enOPT5P5aHARFbtVww==
expo-web-browser@8.3.1:
version "8.3.1"
@ -13189,7 +13189,7 @@ react-native-windows@^0.62.0-0:
uuid "^3.3.2"
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"
resolved "https://codeload.github.com/RocketChat/react-native/tar.gz/616299bbc01eeb56cb7d5b52e543051e1ad2d136"
dependencies:
@ -13832,6 +13832,8 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
rn-extensions-share@RocketChat/rn-extensions-share:
version "2.4.1"
resolved "https://codeload.github.com/RocketChat/rn-extensions-share/tar.gz/4d7c0e4c2f300e4fb116af7b7cc0dbbc8169150c"
dependencies:
react-native "^0.63.1"
rn-fetch-blob@0.12.0:
version "0.12.0"