Compare commits

...

89 Commits

Author SHA1 Message Date
Diego Mello 891b910772
Merge branch 'develop' into chore.try-flashlist 2022-12-15 12:11:58 -03:00
Diego Mello d047cb0c49 Change estimataedItemSize to 48 (short message with avatar) 2022-12-14 15:21:57 -03:00
Diego Mello 83b5e8a98a Stop re-rendering RoomsListView when RoomView is opened on smartphone 2022-12-13 17:28:21 -03:00
Diego Mello d860991c87 Merge branch 'develop' into chore.try-flashlist
# Conflicts:
#	app/containers/MessageActions/Header.tsx
2022-12-13 15:54:15 -03:00
Diego Mello 7e849943fe Close swipeable on recycle 2022-12-13 15:53:04 -03:00
Diego Mello 2b06b01bd6 temp patch 2022-12-12 16:58:25 -03:00
Diego Mello 8678d079bc Fix keyboard on iOS 2022-12-12 16:56:25 -03:00
Diego Mello ff00ad169e ui-lib still 2022-12-09 17:20:24 -03:00
Diego Mello 4e11189f7f Patch-package fix 2022-12-08 18:35:23 -03:00
Diego Mello 428fb5dccc Fix keyboard track dismiss 2022-12-08 18:31:28 -03:00
Diego Mello ab53a5aaea Lint 2022-12-08 18:08:46 -03:00
Diego Mello 4dea3e7ce8 Fix jump to message test 2022-12-08 17:38:11 -03:00
Diego Mello 868bfb7a4c Remove login error test 2022-12-08 17:37:53 -03:00
Diego Mello e088d76724 Fix server dropdown 2022-12-08 16:08:35 -03:00
Diego Mello e6ec8b4e39 Fix emoji picker 2022-12-08 10:51:37 -03:00
Diego Mello 2683976d21 Prevent layout animation after the message has already rendered 2022-12-07 16:54:24 -03:00
Diego Mello 976753c08b Fix empty viewableItems check 2022-12-07 16:28:31 -03:00
Diego Mello fd82771438 Fix list extra data 2022-12-07 16:26:05 -03:00
Diego Mello c610b6d7ec Cleanup 2022-12-07 14:17:08 -03:00
Diego Mello f71be55140 Prevent getSubscription from running on search 2022-12-07 14:01:03 -03:00
Diego Mello 8c9f7ffa54 Bring back sCU to RoomsListView 2022-12-07 13:58:26 -03:00
Diego Mello 001f2a0c17 Cleanup 2022-12-07 13:47:58 -03:00
Diego Mello ffb31d399d Remove insets hoc from RoomsListView 2022-12-07 11:19:19 -03:00
Diego Mello 15bb21eb2f Cleanup 2022-12-07 11:14:08 -03:00
Diego Mello 9788e8a11f Remove width prop drill 2022-12-07 11:11:45 -03:00
Diego Mello 2c62d9cb46 Reenable LayoutAnimation on RoomView, but only for new messages 2022-12-07 10:32:54 -03:00
Diego Mello ae7398233a Stop calling onEndReached unnecessarily 2022-12-06 18:45:10 -03:00
Diego Mello 7b526fe700 Disable RoomView animation and fix console.count 2022-12-06 18:23:59 -03:00
Diego Mello 654feb719c fetchRoomUpdate 2022-12-06 17:10:12 -03:00
Diego Mello dbb9396042 Devtools 2022-12-06 17:06:53 -03:00
Diego Mello c877a0600b Fix setReadOnly calls on RoomView 2022-12-06 15:27:18 -03:00
Diego Mello 696a7e623d Fix RefreshControl on Android 2022-12-05 14:17:43 -03:00
Diego Mello da113ac1d0 Patch FlashList https://github.com/Shopify/flash-list/issues/679#issue-1455946016 2022-12-05 10:04:58 -03:00
Diego Mello 1af737ee71 pods for react-native-flipper-performance 2022-12-05 10:03:48 -03:00
Diego Mello c867f2928f Lint 2022-12-05 09:34:53 -03:00
Diego Mello 87a5793f24 Fix silly usage of extraData on RoomView 2022-12-02 15:14:07 -03:00
Diego Mello f5615c50f6 Export RefreshControl so we don't have to use theme hoc 2022-12-02 13:36:37 -03:00
Diego Mello b2db5500b6 Fix user presence on RoomItem 2022-12-02 11:50:56 -03:00
Diego Mello c6ecfa17a3 Fix Flipper on Android and install react-native-flipper-performance-plugin 2022-12-01 15:17:47 -03:00
Diego Mello 595f010742 Fix nonsense typing 2022-11-30 19:03:55 -03:00
Diego Mello 0c4c361446 Remove isFocused logic from sCU 2022-11-30 18:29:18 -03:00
Diego Mello 53cda7ecb1 Move renderScroll to component based approach.
Declare getItemType.
Add this.props to extraData.
2022-11-30 17:43:11 -03:00
Diego Mello 04e49563f2 Try isFocused on RoomsListView.sCU 2022-11-30 17:15:07 -03:00
Diego Mello 36f0935e55 Reenable layout animation on RoomView 2022-11-30 13:13:52 -03:00
Diego Mello 2cefe12a91 Enable layout animation on RoomsListView 2022-11-30 13:07:00 -03:00
Diego Mello 21dbc7e112 Fix edit, delete, resend and delete after error state 2022-11-29 18:55:02 -03:00
Diego Mello aaef77a58f
Merge branch 'develop' into chore.try-flashlist 2022-11-29 17:54:06 -03:00
Diego Mello cc5fe4c910 Fix pagination on RoomsListView 2022-11-29 17:24:27 -03:00
Diego Mello a593129a3c Fix thread messages grey 2022-11-29 16:43:25 -03:00
Diego Mello de6a897ec4 Cleanup 2022-11-29 16:31:33 -03:00
Diego Mello d42f623a2f Disable layout animation 2022-11-29 16:30:09 -03:00
Diego Mello 4aeda3f778 Try to fix LayoutAnimation on RoomsListView 2022-11-29 16:11:50 -03:00
Diego Mello 34a30214c2 Cleanup message 2022-11-29 15:34:16 -03:00
Diego Mello 79be07072c Merge branch 'develop' into chore.try-flashlist 2022-11-29 15:13:07 -03:00
Diego Mello a73a535221 Fix lint 2022-11-29 15:06:04 -03:00
Diego Mello 4ca5e3a0a7 Remove TAnyMessageModel completely 2022-11-29 14:57:30 -03:00
Diego Mello c0b3daa225 Remove ts-ignore 2022-11-29 14:51:46 -03:00
Diego Mello a130583ff4 Move Message interface to own file 2022-11-29 14:48:34 -03:00
Diego Mello 0b7c075560 TAnyMessageModel -> TAnyMessage on MessageActions 2022-11-29 14:36:11 -03:00
Diego Mello edc67d424b Fix quote 2022-11-29 13:26:58 -03:00
Diego Mello fad04765d4 Add asPlain to messages 2022-11-29 10:58:57 -03:00
Diego Mello 7beaa62003 Fix fav 2022-11-28 18:38:51 -03:00
Diego Mello f67a4010bc Rollback message list padding 2022-11-28 18:35:56 -03:00
Diego Mello 2ee4efd255 Merge branch 'develop' into chore.try-flashlist 2022-11-28 18:22:11 -03:00
Diego Mello 4532a6929c Fix types and remove omichannelsUpdate 2022-11-28 17:23:19 -03:00
Diego Mello 71dc0b8f50 Remove memo from RoomItem 2022-11-28 17:15:13 -03:00
Diego Mello 8ab84541f3 Fully remove sCU 2022-11-28 17:11:52 -03:00
Diego Mello 363331c953 Cleanup 2022-11-28 17:09:01 -03:00
Diego Mello 360afcdd45 Add asPlain to ISubscription 2022-11-28 17:08:53 -03:00
Diego Mello 83701d002f Fix observeRoom logic 2022-11-28 16:54:08 -03:00
Diego Mello c0d5a84ccd First "asPlain" test 2022-11-28 16:28:30 -03:00
Diego Mello 6b7b01af8d Fix toggle read 2022-11-28 15:44:14 -03:00
Diego Mello bac9eb79bb Lint 2022-11-25 17:39:04 -03:00
Diego Mello b9cafe9d31 Fixing RoomsListView 2022-11-25 17:34:49 -03:00
Diego Mello a38bc37aaa animation fix and minor cleanup 2022-11-25 16:32:25 -03:00
Diego Mello 490e567c85 Lint 2022-11-25 16:20:21 -03:00
Diego Mello 45042661c5 Reenable layout animations 2022-11-25 16:17:05 -03:00
Diego Mello e0b5030f6a Fix infinite loading indicator 2022-11-25 16:14:50 -03:00
Diego Mello 1ea3916e94 Fix load more 2022-11-25 15:56:13 -03:00
Diego Mello 537cd0032e Fix message update 2022-11-25 14:52:03 -03:00
Diego Mello e4f0fdb525 Cleanup on RoomsListView 2022-11-25 11:01:46 -03:00
Diego Mello 86a7297abc Cleanup 2022-11-25 10:57:50 -03:00
Diego Mello 34f2da40aa Add scrollViewNativeID to keyboard libs 2022-11-24 14:59:36 -03:00
Diego Mello 0317c4c27a Remove RefreshControl logic platform specific 2022-11-24 10:29:19 -03:00
Diego Mello 898a4a398b Merge branch 'develop' into chore.try-flashlist
# Conflicts:
#	app/containers/message/index.tsx
#	app/views/RoomView/List/List.tsx
2022-11-21 18:25:08 -03:00
Diego Mello 6bb7db9993 Fix lint 2022-09-19 18:09:30 -03:00
Diego Mello 86d5529cfd RoomView 2022-09-19 17:46:15 -03:00
Diego Mello 7bd4182bf7 Try it on RoomsListView 2022-09-19 17:08:25 -03:00
Diego Mello b20c9b1632 Install FlashList 2022-09-19 17:08:11 -03:00
54 changed files with 2155 additions and 1178 deletions

View File

@ -1,25 +0,0 @@
package chat.rocket.reactnative;
import android.content.Context;
import com.facebook.react.ReactInstanceManager;
public class MainDebugApplication extends MainApplication {
@Override
public void onCreate() {
super.onCreate();
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
ReactNativeFlipper.initializeFlipper(context, reactInstanceManager);
}
}

View File

@ -23,6 +23,8 @@ import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.NetworkingModule;
import com.facebook.react.modules.network.CustomClientBuilder;
import tech.bam.rnperformance.flipper.RNPerfMonitorPlugin;
import okhttp3.OkHttpClient;
public class ReactNativeFlipper {
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
@ -33,6 +35,8 @@ public class ReactNativeFlipper {
client.addPlugin(new DatabasesFlipperPlugin(context));
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
client.addPlugin(CrashReporterPlugin.getInstance());
client.addPlugin(new RNPerfMonitorPlugin(reactInstanceManager));
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
NetworkingModule.setCustomClientBuilder(
new CustomClientBuilder() {

View File

@ -9,13 +9,16 @@ import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.ReactInstanceManager;
import com.facebook.soloader.SoLoader;
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
import com.facebook.react.bridge.JSIModulePackage;
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
import android.content.Context;
import android.content.res.Configuration;
import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
@ -76,6 +79,7 @@ public class MainApplication extends Application implements ReactApplication {
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
ApplicationLifecycleDispatcher.onApplicationCreate(this);
}
@ -84,4 +88,35 @@ public class MainApplication extends Application implements ReactApplication {
super.onConfigurationChanged(newConfig);
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("chat.rocket.reactnative.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}

View File

@ -23,7 +23,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
FLIPPER_VERSION=0.125.0
FLIPPER_VERSION=0.175.0
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using

View File

@ -10,12 +10,12 @@ import { useFrequentlyUsedEmoji } from '../../lib/hooks';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
import { useDimensions } from '../../dimensions';
import sharedStyles from '../../views/Styles';
import { IEmoji, TAnyMessageModel } from '../../definitions';
import { IEmoji, TAnyMessage } from '../../definitions';
import Touch from '../Touch';
export interface IHeader {
handleReaction: (emoji: IEmoji | null, message: TAnyMessageModel) => void;
message: TAnyMessageModel;
handleReaction: (emoji: IEmoji | null, message: TAnyMessage) => void;
message: TAnyMessage;
isMasterDetail: boolean;
}

View File

@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import moment from 'moment';
import database from '../../lib/database';
import { getMessageById } from '../../lib/database/services/Message';
import I18n from '../../i18n';
import log, { logEvent } from '../../lib/methods/helpers/log';
import Navigation from '../../lib/navigation/appNavigation';
@ -15,7 +16,7 @@ import { showConfirmationAlert } from '../../lib/methods/helpers/info';
import { TActionSheetOptionsItem, useActionSheet, ACTION_SHEET_ANIMATION_DURATION } from '../ActionSheet';
import Header, { HEADER_HEIGHT, IHeader } from './Header';
import events from '../../lib/methods/helpers/log/events';
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
import { IApplicationState, IEmoji, ILoggedUser, TAnyMessage, TSubscriptionModel } from '../../definitions';
import { getPermalinkMessage } from '../../lib/methods';
import { getRoomTitle, getUidDirectMessage, hasPermission } from '../../lib/methods/helpers';
import { Services } from '../../lib/services';
@ -24,10 +25,10 @@ export interface IMessageActionsProps {
room: TSubscriptionModel;
tmid?: string;
user: Pick<ILoggedUser, 'id'>;
editInit: (message: TAnyMessageModel) => void;
reactionInit: (message: TAnyMessageModel) => void;
editInit: (message: TAnyMessage) => void;
reactionInit: (message: TAnyMessage) => void;
onReactionPress: (shortname: IEmoji, messageId: string) => void;
replyInit: (message: TAnyMessageModel, mention: boolean) => void;
replyInit: (message: TAnyMessage, mention: boolean) => void;
isMasterDetail: boolean;
isReadOnly: boolean;
Message_AllowDeleting?: boolean;
@ -46,7 +47,7 @@ export interface IMessageActionsProps {
}
export interface IMessageActions {
showMessageActions: (message: TAnyMessageModel) => Promise<void>;
showMessageActions: (message: TAnyMessage) => Promise<void>;
}
const MessageActions = React.memo(
@ -109,9 +110,9 @@ const MessageActions = React.memo(
}
};
const isOwn = (message: TAnyMessageModel) => message.u && message.u._id === user.id;
const isOwn = (message: TAnyMessage) => message.u && message.u._id === user.id;
const allowEdit = (message: TAnyMessageModel) => {
const allowEdit = (message: TAnyMessage) => {
if (isReadOnly) {
return false;
}
@ -135,7 +136,7 @@ const MessageActions = React.memo(
return true;
};
const allowDelete = (message: TAnyMessageModel) => {
const allowDelete = (message: TAnyMessage) => {
if (isReadOnly) {
return false;
}
@ -166,19 +167,19 @@ const MessageActions = React.memo(
return true;
};
const getPermalink = (message: TAnyMessageModel) => getPermalinkMessage(message);
const getPermalink = (message: TAnyMessage) => getPermalinkMessage(message);
const handleReply = (message: TAnyMessageModel) => {
const handleReply = (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_REPLY);
replyInit(message, true);
};
const handleEdit = (message: TAnyMessageModel) => {
const handleEdit = (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_EDIT);
editInit(message);
};
const handleCreateDiscussion = (message: TAnyMessageModel) => {
const handleCreateDiscussion = (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_DISCUSSION);
const params = { message, channel: room, showCloseModal: true };
if (isMasterDetail) {
@ -188,7 +189,7 @@ const MessageActions = React.memo(
}
};
const handleUnread = async (message: TAnyMessageModel) => {
const handleUnread = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_UNREAD);
const { id: messageId, ts } = message;
const { rid } = room;
@ -213,7 +214,7 @@ const MessageActions = React.memo(
}
};
const handlePermalink = async (message: TAnyMessageModel) => {
const handlePermalink = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
try {
const permalink = await getPermalink(message);
@ -224,13 +225,13 @@ const MessageActions = React.memo(
}
};
const handleCopy = async (message: TAnyMessageModel) => {
const handleCopy = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_COPY);
await Clipboard.setString((message?.attachments?.[0]?.description || message.msg) ?? '');
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
};
const handleShare = async (message: TAnyMessageModel) => {
const handleShare = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_SHARE);
try {
const permalink = await getPermalink(message);
@ -242,12 +243,12 @@ const MessageActions = React.memo(
}
};
const handleQuote = (message: TAnyMessageModel) => {
const handleQuote = (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_QUOTE);
replyInit(message, false);
};
const handleReplyInDM = async (message: TAnyMessageModel) => {
const handleReplyInDM = async (message: TAnyMessage) => {
if (message?.u?.username) {
const result = await Services.createDirectMessage(message.u.username);
if (result.success) {
@ -264,7 +265,7 @@ const MessageActions = React.memo(
}
};
const handleStar = async (message: TAnyMessageModel) => {
const handleStar = async (message: TAnyMessage) => {
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
try {
await Services.toggleStarMessage(message.id, message.starred as boolean); // TODO: reevaluate `message.starred` type on IMessage
@ -275,7 +276,7 @@ const MessageActions = React.memo(
}
};
const handlePin = async (message: TAnyMessageModel) => {
const handlePin = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_PIN);
try {
await Services.togglePinMessage(message.id, message.pinned as boolean); // TODO: reevaluate `message.pinned` type on IMessage
@ -295,7 +296,7 @@ const MessageActions = React.memo(
hideActionSheet();
};
const handleReadReceipt = (message: TAnyMessageModel) => {
const handleReadReceipt = (message: TAnyMessage) => {
if (isMasterDetail) {
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
} else {
@ -303,14 +304,18 @@ const MessageActions = React.memo(
}
};
const handleToggleTranslation = async (message: TAnyMessageModel) => {
const handleToggleTranslation = async (message: TAnyMessage) => {
try {
if (!room.autoTranslateLanguage) {
return;
}
const db = database.active;
const messageRecord = await getMessageById(message.id);
if (!messageRecord) {
return;
}
await db.write(async () => {
await message.update(m => {
await messageRecord.update(m => {
m.autoTranslate = !m.autoTranslate;
m._updatedAt = new Date();
});
@ -324,7 +329,7 @@ const MessageActions = React.memo(
}
};
const handleReport = async (message: TAnyMessageModel) => {
const handleReport = async (message: TAnyMessage) => {
logEvent(events.ROOM_MSG_ACTION_REPORT);
try {
await Services.reportMessage(message.id);
@ -335,14 +340,14 @@ const MessageActions = React.memo(
}
};
const handleDelete = (message: TAnyMessageModel) => {
const handleDelete = (message: TAnyMessage) => {
showConfirmationAlert({
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
confirmationText: I18n.t('Delete'),
onPress: async () => {
try {
logEvent(events.ROOM_MSG_ACTION_DELETE);
await Services.deleteMessage(message.id, message.subscription ? message.subscription.id : '');
await Services.deleteMessage(message.id, message.rid);
} catch (e) {
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
log(e);
@ -351,7 +356,7 @@ const MessageActions = React.memo(
});
};
const getOptions = (message: TAnyMessageModel) => {
const getOptions = (message: TAnyMessage) => {
const options: TActionSheetOptionsItem[] = [];
const videoConfBlock = message.t === 'videoconf';
@ -487,7 +492,7 @@ const MessageActions = React.memo(
return options;
};
const showMessageActions = async (message: TAnyMessageModel) => {
const showMessageActions = async (message: TAnyMessage) => {
logEvent(events.ROOM_SHOW_MSG_ACTIONS);
await getPermissions();
showActionSheet({

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { Alert, Keyboard, NativeModules, Text, View, BackHandler } from 'react-native';
import { connect } from 'react-redux';
// import { KeyboardTrackingView } from 'react-native-keyboard-tracking-view';
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
import { dequal } from 'dequal';
@ -169,7 +170,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
id: ''
},
sharing: false,
iOSScrollBehavior: NativeModules.KeyboardTrackingViewTempManager?.KeyboardTrackingScrollBehaviorFixedOffset,
iOSScrollBehavior: NativeModules.KeyboardTrackingViewTempManager?.KeyboardTrackingScrollBehaviorScrollToBottomInvertedOnly,
isActionsEnabled: true,
getCustomEmoji: () => {}
};
@ -1290,7 +1291,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
render() {
console.count(`${this.constructor.name}.render calls`);
const { showEmojiKeyboard } = this.state;
const { user, baseUrl, theme, iOSScrollBehavior } = this.props;
const { user, baseUrl, theme, iOSScrollBehavior, tmid, rid } = this.props;
return (
<MessageboxContext.Provider
value={{
@ -1309,11 +1310,10 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
kbInitialProps={{ theme }}
onKeyboardResigned={this.onKeyboardResigned}
onItemSelected={this.onKeyboardItemSelected}
trackInteractive
requiresSameParentToManageScrollView
addBottomView
bottomViewColor={themes[theme].messageboxBackground}
iOSScrollBehavior={iOSScrollBehavior}
scrollViewNativeID={tmid || rid}
/>
</MessageboxContext.Provider>
);

View File

@ -1,6 +1,8 @@
import { forwardRef, useImperativeHandle } from 'react';
import Model from '@nozbe/watermelondb/Model';
import { getMessageById } from '../lib/database/services/Message';
import { getThreadMessageById } from '../lib/database/services/ThreadMessage';
import database from '../lib/database';
import protectedFunction from '../lib/methods/helpers/protectedFunction';
import { useActionSheet } from './ActionSheet';
@ -27,16 +29,16 @@ const MessageErrorActions = forwardRef<IMessageErrorActions, { tmid?: string }>(
const msgCollection = db.get('messages');
const threadCollection = db.get('threads');
// Delete the object (it can be Message or ThreadMessage instance)
deleteBatch.push(message.prepareDestroyPermanently());
const msg = await getMessageById(message.id);
if (msg) {
deleteBatch.push(msg.prepareDestroyPermanently());
}
// If it's a thread, we find and delete the whole tree, if necessary
if (tmid) {
try {
const msg = await msgCollection.find(message.id);
const msg = await getThreadMessageById(message.id);
if (msg) {
deleteBatch.push(msg.prepareDestroyPermanently());
} catch {
// Do nothing: message not found
}
try {

View File

@ -1,5 +1,4 @@
import React from 'react';
import { dequal } from 'dequal';
import I18n from '../../i18n';
import styles from './styles';
@ -45,9 +44,7 @@ const formatMsg = ({ lastMessage, type, showLastMessage, username, useRealName }
return `${prefix}${lastMessage.msg}`;
};
const arePropsEqual = (oldProps: any, newProps: any) => dequal(oldProps, newProps);
const LastMessage = React.memo(({ lastMessage, type, showLastMessage, username, alert, useRealName }: ILastMessageProps) => {
const LastMessage = ({ lastMessage, type, showLastMessage, username, alert, useRealName }: ILastMessageProps) => {
const { colors } = useTheme();
return (
<MarkdownPreview
@ -63,6 +60,6 @@ const LastMessage = React.memo(({ lastMessage, type, showLastMessage, username,
testID='room-item-last-message'
/>
);
}, arePropsEqual);
};
export default LastMessage;

View File

@ -20,7 +20,6 @@ const RoomItem = ({
prid,
name,
avatar,
width,
username,
showLastMessage,
status = 'offline',
@ -52,12 +51,13 @@ const RoomItem = ({
showAvatar,
displayMode,
sourceType,
hideMentionStatus
hideMentionStatus,
touchableRef
}: IRoomItemProps) => (
<Touchable
ref={touchableRef}
onPress={onPress}
onLongPress={onLongPress}
width={width}
favorite={favorite}
toggleFav={toggleFav}
isRead={isRead}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { forwardRef, useImperativeHandle } from 'react';
import Animated, {
useAnimatedGestureHandler,
useSharedValue,
@ -13,227 +13,242 @@ import {
HandlerStateChangeEventPayload,
PanGestureHandlerEventPayload
} from 'react-native-gesture-handler';
import { useWindowDimensions } from 'react-native';
import Touch from '../Touch';
import { ACTION_WIDTH, LONG_SWIPE, SMALL_SWIPE } from './styles';
import { LeftActions, RightActions } from './Actions';
import { ITouchableProps } from './interfaces';
import { ITouchableProps, ITouchableRef } from './interfaces';
import { useTheme } from '../../theme';
import I18n from '../../i18n';
import { MAX_SIDEBAR_WIDTH } from '../../lib/constants';
import { useAppSelector } from '../../lib/hooks';
const Touchable = ({
children,
type,
onPress,
onLongPress,
testID,
width,
favorite,
isRead,
rid,
toggleFav,
toggleRead,
hideChannel,
isFocused,
swipeEnabled,
displayMode
}: ITouchableProps): React.ReactElement => {
const { colors } = useTheme();
const Touchable = forwardRef<ITouchableRef, ITouchableProps>(
(
{
children,
type,
onPress,
onLongPress,
testID,
favorite,
isRead,
rid,
toggleFav,
toggleRead,
hideChannel,
isFocused,
swipeEnabled,
displayMode
},
ref
): React.ReactElement => {
const { colors } = useTheme();
const { width: deviceWidth } = useWindowDimensions();
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
const width = isMasterDetail ? MAX_SIDEBAR_WIDTH : deviceWidth;
const rowOffSet = useSharedValue(0);
const transX = useSharedValue(0);
const rowState = useSharedValue(0); // 0: closed, 1: right opened, -1: left opened
let _value = 0;
const rowOffSet = useSharedValue(0);
const transX = useSharedValue(0);
const rowState = useSharedValue(0); // 0: closed, 1: right opened, -1: left opened
let _value = 0;
const close = () => {
rowState.value = 0;
transX.value = withSpring(0, { overshootClamping: true });
rowOffSet.value = 0;
};
const close = () => {
console.log(`${rid} close`);
rowState.value = 0;
transX.value = withSpring(0, { overshootClamping: true });
rowOffSet.value = 0;
};
const handleToggleFav = () => {
if (toggleFav) {
toggleFav(rid, favorite);
}
close();
};
useImperativeHandle(ref, () => ({
close
}));
const handleToggleRead = () => {
if (toggleRead) {
toggleRead(rid, isRead);
}
};
const handleHideChannel = () => {
if (hideChannel) {
hideChannel(rid, type);
}
};
const onToggleReadPress = () => {
handleToggleRead();
close();
};
const onHidePress = () => {
handleHideChannel();
close();
};
const handlePress = () => {
if (rowState.value !== 0) {
const handleToggleFav = () => {
if (toggleFav) {
toggleFav(rid, favorite);
}
close();
return;
}
if (onPress) {
onPress();
}
};
};
const handleLongPress = () => {
if (rowState.value !== 0) {
const handleToggleRead = () => {
if (toggleRead) {
toggleRead(rid, isRead);
}
};
const handleHideChannel = () => {
if (hideChannel) {
hideChannel(rid, type);
}
};
const onToggleReadPress = () => {
handleToggleRead();
close();
return;
}
};
if (onLongPress) {
onLongPress();
}
};
const onHidePress = () => {
handleHideChannel();
close();
};
const onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
if (nativeEvent.state === State.ACTIVE) {
handleLongPress();
}
};
const handlePress = () => {
if (rowState.value !== 0) {
close();
return;
}
if (onPress) {
onPress();
}
};
const handleRelease = (event: PanGestureHandlerEventPayload) => {
const { translationX } = event;
_value += translationX;
let toValue = 0;
if (rowState.value === 0) {
// if no option is opened
if (translationX > 0 && translationX < LONG_SWIPE) {
if (I18n.isRTL) {
const handleLongPress = () => {
if (rowState.value !== 0) {
close();
return;
}
if (onLongPress) {
onLongPress();
}
};
const onLongPressHandlerStateChange = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => {
if (nativeEvent.state === State.ACTIVE) {
handleLongPress();
}
};
const handleRelease = (event: PanGestureHandlerEventPayload) => {
const { translationX } = event;
_value += translationX;
let toValue = 0;
if (rowState.value === 0) {
// if no option is opened
if (translationX > 0 && translationX < LONG_SWIPE) {
if (I18n.isRTL) {
toValue = 2 * ACTION_WIDTH;
} else {
toValue = ACTION_WIDTH;
}
rowState.value = -1;
} else if (translationX >= LONG_SWIPE) {
toValue = 0;
if (I18n.isRTL) {
handleHideChannel();
} else {
handleToggleRead();
}
} else if (translationX < 0 && translationX > -LONG_SWIPE) {
// open trailing option if he swipe left
if (I18n.isRTL) {
toValue = -ACTION_WIDTH;
} else {
toValue = -2 * ACTION_WIDTH;
}
rowState.value = 1;
} else if (translationX <= -LONG_SWIPE) {
toValue = 0;
rowState.value = 1;
if (I18n.isRTL) {
handleToggleRead();
} else {
handleHideChannel();
}
} else {
toValue = 0;
}
} else if (rowState.value === -1) {
// if left option is opened
if (_value < SMALL_SWIPE) {
toValue = 0;
rowState.value = 0;
} else if (_value > LONG_SWIPE) {
toValue = 0;
rowState.value = 0;
if (I18n.isRTL) {
handleHideChannel();
} else {
handleToggleRead();
}
} else if (I18n.isRTL) {
toValue = 2 * ACTION_WIDTH;
} else {
toValue = ACTION_WIDTH;
}
rowState.value = -1;
} else if (translationX >= LONG_SWIPE) {
toValue = 0;
if (I18n.isRTL) {
handleHideChannel();
} else {
handleToggleRead();
}
} else if (translationX < 0 && translationX > -LONG_SWIPE) {
// open trailing option if he swipe left
if (I18n.isRTL) {
} else if (rowState.value === 1) {
// if right option is opened
if (_value > -2 * SMALL_SWIPE) {
toValue = 0;
rowState.value = 0;
} else if (_value < -LONG_SWIPE) {
if (I18n.isRTL) {
handleToggleRead();
} else {
handleHideChannel();
}
} else if (I18n.isRTL) {
toValue = -ACTION_WIDTH;
} else {
toValue = -2 * ACTION_WIDTH;
}
rowState.value = 1;
} else if (translationX <= -LONG_SWIPE) {
toValue = 0;
rowState.value = 1;
if (I18n.isRTL) {
handleToggleRead();
} else {
handleHideChannel();
}
} else {
toValue = 0;
}
} else if (rowState.value === -1) {
// if left option is opened
if (_value < SMALL_SWIPE) {
toValue = 0;
rowState.value = 0;
} else if (_value > LONG_SWIPE) {
toValue = 0;
rowState.value = 0;
if (I18n.isRTL) {
handleHideChannel();
} else {
handleToggleRead();
}
} else if (I18n.isRTL) {
toValue = 2 * ACTION_WIDTH;
} else {
toValue = ACTION_WIDTH;
transX.value = withSpring(toValue, { overshootClamping: true });
rowOffSet.value = toValue;
_value = toValue;
};
const onGestureEvent = useAnimatedGestureHandler({
onActive: event => {
transX.value = event.translationX + rowOffSet.value;
if (transX.value > 2 * width) transX.value = 2 * width;
},
onEnd: event => {
runOnJS(handleRelease)(event);
}
} else if (rowState.value === 1) {
// if right option is opened
if (_value > -2 * SMALL_SWIPE) {
toValue = 0;
rowState.value = 0;
} else if (_value < -LONG_SWIPE) {
if (I18n.isRTL) {
handleToggleRead();
} else {
handleHideChannel();
}
} else if (I18n.isRTL) {
toValue = -ACTION_WIDTH;
} else {
toValue = -2 * ACTION_WIDTH;
}
}
transX.value = withSpring(toValue, { overshootClamping: true });
rowOffSet.value = toValue;
_value = toValue;
};
});
const onGestureEvent = useAnimatedGestureHandler({
onActive: event => {
transX.value = event.translationX + rowOffSet.value;
if (transX.value > 2 * width) transX.value = 2 * width;
},
onEnd: event => {
runOnJS(handleRelease)(event);
}
});
const animatedStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] }));
const animatedStyles = useAnimatedStyle(() => ({ transform: [{ translateX: transX.value }] }));
return (
<LongPressGestureHandler onHandlerStateChange={onLongPressHandlerStateChange}>
<Animated.View>
<PanGestureHandler activeOffsetX={[-20, 20]} onGestureEvent={onGestureEvent} enabled={swipeEnabled}>
<Animated.View>
<LeftActions
transX={transX}
isRead={isRead}
width={width}
onToggleReadPress={onToggleReadPress}
displayMode={displayMode}
/>
<RightActions
transX={transX}
favorite={favorite}
width={width}
toggleFav={handleToggleFav}
onHidePress={onHidePress}
displayMode={displayMode}
/>
<Animated.View style={animatedStyles}>
<Touch
onPress={handlePress}
testID={testID}
style={{
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
}}
>
{children}
</Touch>
return (
<LongPressGestureHandler onHandlerStateChange={onLongPressHandlerStateChange}>
<Animated.View>
<PanGestureHandler activeOffsetX={[-20, 20]} onGestureEvent={onGestureEvent} enabled={swipeEnabled}>
<Animated.View>
<LeftActions
transX={transX}
isRead={isRead}
width={width}
onToggleReadPress={onToggleReadPress}
displayMode={displayMode}
/>
<RightActions
transX={transX}
favorite={favorite}
width={width}
toggleFav={handleToggleFav}
onHidePress={onHidePress}
displayMode={displayMode}
/>
<Animated.View style={animatedStyles}>
<Touch
onPress={handlePress}
testID={testID}
style={{
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
}}
>
{children}
</Touch>
</Animated.View>
</Animated.View>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</LongPressGestureHandler>
);
};
</PanGestureHandler>
</Animated.View>
</LongPressGestureHandler>
);
}
);
export default Touchable;

View File

@ -1,136 +1,127 @@
import React, { useEffect, useReducer, useRef } from 'react';
import { Subscription } from 'rxjs';
import React, { useEffect, useRef } from 'react';
import I18n from '../../i18n';
import { useAppSelector } from '../../lib/hooks';
import { getUserPresence } from '../../lib/methods';
import { isGroupChat } from '../../lib/methods/helpers';
import { formatDate } from '../../lib/methods/helpers/room';
import { IRoomItemContainerProps } from './interfaces';
import { IRoomItemContainerProps, ITouchableRef } from './interfaces';
import RoomItem from './RoomItem';
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
const attrs = ['width', 'isFocused', 'showLastMessage', 'autoJoin', 'showAvatar', 'displayMode'];
const RoomItemContainer = ({
item,
id,
onPress,
onLongPress,
toggleFav,
toggleRead,
hideChannel,
isFocused,
showLastMessage,
username,
useRealName,
autoJoin,
showAvatar,
displayMode,
getRoomTitle = () => 'title',
getRoomAvatar = () => '',
getIsRead = () => false,
swipeEnabled = true
}: IRoomItemContainerProps): React.ReactElement => {
const name = getRoomTitle(item);
const testID = `rooms-list-view-item-${name}`;
const avatar = getRoomAvatar(item);
const isRead = getIsRead(item);
const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt);
const alert = item.alert || item.tunread?.length;
const connected = useAppSelector(state => state.meteor.connected);
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
const isDirect = !!(item.t === 'd' && id && !isGroupChat(item));
const touchableRef = useRef<ITouchableRef>(null);
const RoomItemContainer = React.memo(
({
item,
id,
onPress,
onLongPress,
width,
toggleFav,
toggleRead,
hideChannel,
isFocused,
showLastMessage,
username,
useRealName,
autoJoin,
showAvatar,
displayMode,
getRoomTitle = () => 'title',
getRoomAvatar = () => '',
getIsRead = () => false,
swipeEnabled = true
}: IRoomItemContainerProps) => {
const name = getRoomTitle(item);
const testID = `rooms-list-view-item-${name}`;
const avatar = getRoomAvatar(item);
const isRead = getIsRead(item);
const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt);
const alert = item.alert || item.tunread?.length;
const connected = useAppSelector(state => state.meteor.connected);
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
const [_, forceUpdate] = useReducer(x => x + 1, 1);
const roomSubscription = useRef<Subscription | null>(null);
useEffect(() => {
const init = () => {
if (item?.observe) {
const observable = item.observe();
roomSubscription.current = observable?.subscribe?.(() => {
if (_) forceUpdate();
});
}
};
init();
return () => roomSubscription.current?.unsubscribe();
}, []);
useEffect(() => {
const isDirect = !!(item.t === 'd' && id && !isGroupChat(item));
if (connected && isDirect) {
getUserPresence(id);
}
}, [connected]);
const handleOnPress = () => onPress(item);
const handleOnLongPress = () => onLongPress && onLongPress(item);
let accessibilityLabel = '';
if (item.unread === 1) {
accessibilityLabel = `, ${item.unread} ${I18n.t('alert')}`;
} else if (item.unread > 1) {
accessibilityLabel = `, ${item.unread} ${I18n.t('alerts')}`;
// When app reconnects, we need to fetch the rendered user's presence
useEffect(() => {
if (connected && isDirect) {
getUserPresence(id);
}
if (item.userMentions > 0) {
accessibilityLabel = `, ${I18n.t('you_were_mentioned')}`;
}
if (date) {
accessibilityLabel = `, ${I18n.t('last_message')} ${date}`;
}, [connected]);
/**
* The component can be recycled by FlashList.
* When rid changes and there's no user's status, we need to fetch it
*/
useEffect(() => {
if (!userStatus && isDirect) {
getUserPresence(id);
}
const status = item.t === 'l' ? item.visitor?.status || item.v?.status : userStatus;
// TODO: Remove this when we have a better way to close the swipeable
touchableRef?.current?.close();
}, [item.rid]);
return (
<RoomItem
name={name}
avatar={avatar}
isGroupChat={isGroupChat(item)}
isRead={isRead}
onPress={handleOnPress}
onLongPress={handleOnLongPress}
date={date}
accessibilityLabel={accessibilityLabel}
width={width}
favorite={item.f}
rid={item.rid}
toggleFav={toggleFav}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={item.t}
isFocused={isFocused}
prid={item.prid}
status={status}
hideUnreadStatus={item.hideUnreadStatus}
hideMentionStatus={item.hideMentionStatus}
alert={alert}
lastMessage={item.lastMessage}
showLastMessage={showLastMessage}
username={username}
useRealName={useRealName}
unread={item.unread}
userMentions={item.userMentions}
groupMentions={item.groupMentions}
tunread={item.tunread}
tunreadUser={item.tunreadUser}
tunreadGroup={item.tunreadGroup}
swipeEnabled={swipeEnabled}
teamMain={item.teamMain}
autoJoin={autoJoin}
showAvatar={showAvatar}
displayMode={displayMode}
sourceType={item.source}
/>
);
},
(props, nextProps) => attrs.every(key => props[key] === nextProps[key])
);
const handleOnPress = () => onPress(item);
const handleOnLongPress = () => onLongPress && onLongPress(item);
let accessibilityLabel = '';
if (item.unread === 1) {
accessibilityLabel = `, ${item.unread} ${I18n.t('alert')}`;
} else if (item.unread > 1) {
accessibilityLabel = `, ${item.unread} ${I18n.t('alerts')}`;
}
if (item.userMentions > 0) {
accessibilityLabel = `, ${I18n.t('you_were_mentioned')}`;
}
if (date) {
accessibilityLabel = `, ${I18n.t('last_message')} ${date}`;
}
const status = item.t === 'l' ? item.visitor?.status || item.v?.status : userStatus;
return (
<RoomItem
touchableRef={touchableRef}
name={name}
avatar={avatar}
isGroupChat={isGroupChat(item)}
isRead={isRead}
onPress={handleOnPress}
onLongPress={handleOnLongPress}
date={date}
accessibilityLabel={accessibilityLabel}
favorite={item.f}
rid={item.rid}
toggleFav={toggleFav}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={item.t}
isFocused={isFocused}
prid={item.prid}
status={status}
hideUnreadStatus={item.hideUnreadStatus}
hideMentionStatus={item.hideMentionStatus}
alert={alert}
lastMessage={item.lastMessage}
showLastMessage={showLastMessage}
username={username}
useRealName={useRealName}
unread={item.unread}
userMentions={item.userMentions}
groupMentions={item.groupMentions}
tunread={item.tunread}
tunreadUser={item.tunreadUser}
tunreadGroup={item.tunreadGroup}
swipeEnabled={swipeEnabled}
teamMain={item.teamMain}
autoJoin={autoJoin}
showAvatar={showAvatar}
displayMode={displayMode}
sourceType={item.source}
/>
);
};
export default RoomItemContainer;

View File

@ -78,7 +78,6 @@ interface IBaseRoomItem extends IRoomItemTouchables {
showAvatar: boolean;
swipeEnabled: boolean;
autoJoin?: boolean;
width: number;
username?: string;
}
@ -116,6 +115,7 @@ export interface IRoomItemProps extends IBaseRoomItem {
size?: number;
sourceType: IOmnichannelSource;
hideMentionStatus?: boolean;
touchableRef: React.RefObject<ITouchableRef>;
}
export interface ILastMessageProps {
@ -127,11 +127,14 @@ export interface ILastMessageProps {
alert: boolean;
}
export interface ITouchableRef {
close: () => void;
}
export interface ITouchableProps extends IRoomItemTouchables {
children: JSX.Element;
type: SubscriptionType;
testID: string;
width: number;
favorite: boolean;
isRead: boolean;
rid: string;

View File

@ -68,7 +68,7 @@ const Attachments: React.FC<IMessageAttachments> = React.memo(
if (file && file.image_url) {
return (
<Image
key={file.image_url}
key={index}
file={file}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}
@ -81,7 +81,7 @@ const Attachments: React.FC<IMessageAttachments> = React.memo(
if (file && file.audio_url) {
return (
<Audio
key={file.audio_url}
key={index}
file={file}
getCustomEmoji={getCustomEmoji}
isReply={isReply}
@ -95,7 +95,7 @@ const Attachments: React.FC<IMessageAttachments> = React.memo(
if (file.video_url) {
return (
<Video
key={file.video_url}
key={index}
file={file}
showAttachment={showAttachment}
getCustomEmoji={getCustomEmoji}

View File

@ -90,8 +90,8 @@ const Fields = React.memo(
return (
<>
{attachment.fields.map(field => (
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
{attachment.fields.map((field, index) => (
<View key={index} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
<Text testID='collapsibleQuoteTouchableFieldTitle' style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>
{field.title}
</Text>

View File

@ -10,7 +10,7 @@ const Emoji = React.memo(
const parsedContent = content.replace(/^:|:$/g, '');
const emoji = getCustomEmoji(parsedContent);
if (emoji) {
return <CustomEmoji key={content} style={customEmojiStyle} emoji={emoji} />;
return <CustomEmoji style={customEmojiStyle} emoji={emoji} />;
}
return <Text style={standardEmojiStyle}>{shortnameToUnicode(content)}</Text>;
},

View File

@ -53,7 +53,6 @@ const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReacti
<Touchable
onPress={() => onReactionPress(reaction.emoji)}
onLongPress={onReactionLongPress}
key={reaction.emoji}
testID={`message-reaction-${reaction.emoji}`}
style={[
styles.reactionButton,
@ -83,8 +82,8 @@ const Reactions = React.memo(({ reactions, getCustomEmoji }: IMessageReactions)
}
return (
<View style={styles.reactionsContainer}>
{reactions.map(reaction => (
<Reaction key={reaction.emoji} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} />
{reactions.map((reaction, index) => (
<Reaction key={index} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} />
))}
<AddReaction theme={theme} />
</View>

View File

@ -184,8 +184,8 @@ const Fields = React.memo(
return (
<View style={styles.fieldsContainer}>
{attachment.fields.map(field => (
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
{attachment.fields.map((field, index) => (
<View key={index} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
<Markdown msg={field?.value || ''} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
</View>

View File

@ -141,7 +141,7 @@ const Urls = React.memo(
return null;
}
return urls.map((url: IUrl, index: number) => <Url url={url} key={url.url} index={index} theme={theme} />);
return urls.map((url: IUrl, index: number) => <Url url={url} key={index} index={index} theme={theme} />);
},
(oldProps, newProps) => dequal(oldProps.urls, newProps.urls)
);

View File

@ -1,6 +1,5 @@
import React from 'react';
import { Keyboard, ViewStyle } from 'react-native';
import { Subscription } from 'rxjs';
import { Keyboard } from 'react-native';
import Message from './Message';
import MessageContext from './Context';
@ -8,64 +7,11 @@ import { debounce } from '../../lib/methods/helpers';
import { getMessageTranslation } from './utils';
import { TSupportedThemes, withTheme } from '../../theme';
import openLink from '../../lib/methods/helpers/openLink';
import { IAttachment, TAnyMessageModel, TGetCustomEmoji } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
import { IAttachment } from '../../definitions';
import { E2E_MESSAGE_TYPE, E2E_STATUS, messagesStatus } from '../../lib/constants';
import { IMessageContainerProps, TAnyMessageContainerState } from './interfaces';
interface IMessageContainerProps {
item: TAnyMessageModel;
user: {
id: string;
username: string;
token: string;
};
msg?: string;
rid: string;
timeFormat?: string;
style?: ViewStyle;
archived?: boolean;
broadcast?: boolean;
previousItem?: TAnyMessageModel;
baseUrl: string;
Message_GroupingPeriod?: number;
isReadReceiptEnabled?: boolean;
isThreadRoom: boolean;
isSystemMessage?: boolean;
useRealName?: boolean;
autoTranslateRoom?: boolean;
autoTranslateLanguage?: string;
status?: number;
isIgnored?: boolean;
highlighted?: boolean;
getCustomEmoji: TGetCustomEmoji;
onLongPress?: (item: TAnyMessageModel) => void;
onReactionPress?: (emoji: string, id: string) => void;
onEncryptedPress?: () => void;
onDiscussionPress?: (item: TAnyMessageModel) => void;
onThreadPress?: (item: TAnyMessageModel) => void;
errorActionsShow?: (item: TAnyMessageModel) => void;
replyBroadcast?: (item: TAnyMessageModel) => void;
reactionInit?: (item: TAnyMessageModel) => void;
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
showAttachment: (file: IAttachment) => void;
onReactionLongPress?: (item: TAnyMessageModel) => void;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
callJitsi?: () => void;
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
threadBadgeColor?: string;
toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
jumpToMessage?: (link: string) => void;
onPress?: () => void;
theme: TSupportedThemes;
closeEmojiAndAction?: (action?: Function, params?: any) => void;
}
interface IMessageContainerState {
isManualUnignored: boolean;
}
class MessageContainer extends React.Component<IMessageContainerProps, IMessageContainerState> {
class MessageContainer extends React.Component<IMessageContainerProps, TAnyMessageContainerState> {
static defaultProps = {
getCustomEmoji: () => null,
onLongPress: () => {},
@ -79,45 +25,6 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
state = { isManualUnignored: false };
private subscription?: Subscription;
componentDidMount() {
const { item } = this.props;
if (item && item.observe) {
const observable = item.observe();
this.subscription = observable.subscribe(() => {
this.forceUpdate();
});
}
}
shouldComponentUpdate(nextProps: IMessageContainerProps, nextState: IMessageContainerState) {
const { isManualUnignored } = this.state;
const { threadBadgeColor, isIgnored, highlighted, previousItem } = this.props;
if (nextProps.highlighted !== highlighted) {
return true;
}
if (nextProps.threadBadgeColor !== threadBadgeColor) {
return true;
}
if (nextProps.isIgnored !== isIgnored) {
return true;
}
if (nextState.isManualUnignored !== isManualUnignored) {
return true;
}
if (nextProps.previousItem?._id !== previousItem?._id) {
return true;
}
return false;
}
componentWillUnmount() {
if (this.subscription && this.subscription.unsubscribe) {
this.subscription.unsubscribe();
}
}
onPressAction = () => {
const { closeEmojiAndAction } = this.props;
@ -227,11 +134,11 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
try {
if (
previousItem &&
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
// @ts-ignore TODO: TAnyMessage vs TAnyMessageFromServer non-sense
previousItem.ts.toDateString() === item.ts.toDateString() &&
previousItem.u.username === item.u.username &&
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
// @ts-ignore TODO: TAnyMessage vs TAnyMessageFromServer non-sense
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
previousItem.tmid === item.tmid &&
item.t !== 'rm' &&
@ -407,8 +314,8 @@ class MessageContainer extends React.Component<IMessageContainerProps, IMessageC
threadBadgeColor,
toggleFollowThread,
replies
}}>
{/* @ts-ignore*/}
}}
>
<Message
id={id}
msg={message}

View File

@ -1,11 +1,65 @@
import { MarkdownAST } from '@rocket.chat/message-parser';
import { StyleProp, TextStyle } from 'react-native';
import { StyleProp, TextStyle, ViewStyle } from 'react-native';
import { ImageStyle } from 'react-native-fast-image';
import { IUserChannel } from '../markdown/interfaces';
import { TGetCustomEmoji } from '../../definitions/IEmoji';
import { IAttachment, IThread, IUrl, IUserMention, IUserMessage, MessageType, TAnyMessageModel } from '../../definitions';
import { IAttachment, IThread, IUrl, IUserMention, IUserMessage, MessageType, TAnyMessage } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView';
import { TSupportedThemes } from '../../theme';
export interface IMessageContainerProps {
item: TAnyMessage;
user: {
id: string;
username: string;
token: string;
};
msg?: string;
rid: string;
timeFormat?: string;
style?: ViewStyle;
archived?: boolean;
broadcast?: boolean;
previousItem?: TAnyMessage;
baseUrl: string;
Message_GroupingPeriod?: number;
isReadReceiptEnabled?: boolean;
isThreadRoom: boolean;
isSystemMessage?: boolean;
useRealName?: boolean;
autoTranslateRoom?: boolean;
autoTranslateLanguage?: string;
status?: number;
isIgnored?: boolean;
highlighted?: boolean;
getCustomEmoji: TGetCustomEmoji;
onLongPress?: (item: TAnyMessage) => void;
onReactionPress?: (emoji: string, id: string) => void;
onEncryptedPress?: () => void;
onDiscussionPress?: (item: TAnyMessage) => void;
onThreadPress?: (item: TAnyMessage) => void;
errorActionsShow?: (item: TAnyMessage) => void;
replyBroadcast?: (item: TAnyMessage) => void;
reactionInit?: (item: TAnyMessage) => void;
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
showAttachment: (file: IAttachment) => void;
onReactionLongPress?: (item: TAnyMessage) => void;
navToRoomInfo: (navParam: IRoomInfoParam) => void;
callJitsi?: () => void;
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
threadBadgeColor?: string;
toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
jumpToMessage?: (link: string) => void;
onPress?: () => void;
theme: TSupportedThemes;
closeEmojiAndAction?: (action?: Function, params?: any) => void;
}
export interface TAnyMessageContainerState {
isManualUnignored: boolean;
}
export interface IMessageAttachments {
attachments?: IAttachment[];
@ -44,7 +98,6 @@ export interface IMessageCallButton {
}
export interface IMessageContent {
_id: string;
isTemp: boolean;
isInfo: string | boolean;
tmid?: string;
@ -119,7 +172,6 @@ export interface IMessage extends IMessageRepliedThread, IMessageInner, IMessage
hasError: boolean;
style: any;
// style: ViewStyle;
onLongPress?: (item: TAnyMessageModel) => void;
isReadReceiptEnabled?: boolean;
unread?: boolean;
isIgnored: boolean;

View File

@ -1,5 +1,5 @@
/* eslint-disable complexity */
import { MessageTypesValues, TMessageModel } from '../../definitions/IMessage';
import { MessageTypesValues, TAnyMessage } from '../../definitions/IMessage';
import I18n from '../../i18n';
import { DISCUSSION } from './constants';
@ -183,7 +183,7 @@ export const getInfoMessage = ({ type, role, msg, author, comment }: TInfoMessag
}
};
export const getMessageTranslation = (message: TMessageModel, autoTranslateLanguage: string): string | null => {
export const getMessageTranslation = (message: TAnyMessage, autoTranslateLanguage: string): string | null => {
if (!autoTranslateLanguage) {
return null;
}

View File

@ -4,11 +4,18 @@ import { MarkdownAST } from '@rocket.chat/message-parser';
import { MessageTypeLoad } from '../lib/constants';
import { IAttachment } from './IAttachment';
import { IReaction } from './IReaction';
import { TThreadMessageModel } from './IThreadMessage';
import { TThreadModel } from './IThread';
import { IThreadMessage } from './IThreadMessage';
import { IThread } from './IThread';
import { IUrl, IUrlFromServer } from './IUrl';
export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj' | MessageTypeLoad | MessageTypesValues;
export type MessageType =
| 'jitsi_call_started'
| 'discussion-created'
| 'e2e'
| 'rm'
| 'uj'
| MessageTypeLoad
| MessageTypesValues;
export interface IUserMessage {
_id: string;
@ -139,9 +146,12 @@ export interface IMessage extends IMessageFromServer {
editedAt?: string | Date;
}
export type TMessageModel = IMessage & Model;
export type TMessageModel = IMessage &
Model & {
asPlain: () => IMessage;
};
export type TAnyMessageModel = TMessageModel | TThreadModel | TThreadMessageModel;
export type TAnyMessage = IMessage | IThread | IThreadMessage;
export type TTypeMessages = IMessageFromServer | ILoadMoreMessage | IMessage;
// Read receipts to ReadReceiptView and chat.getMessageReadReceipts

View File

@ -96,7 +96,6 @@ export interface ISubscription {
avatarETag?: string;
teamId?: string;
teamMain?: boolean;
unsubscribe: () => Promise<any>;
separator?: boolean;
onHold?: boolean;
source?: IOmnichannelSource;
@ -108,7 +107,10 @@ export interface ISubscription {
uploads: RelationModified<TUploadModel>;
}
export type TSubscriptionModel = ISubscription & Model;
export type TSubscriptionModel = ISubscription &
Model & {
asPlain: () => ISubscription;
};
export type TSubscription = TSubscriptionModel | ISubscription;
// https://github.com/RocketChat/Rocket.Chat/blob/a88a96fcadd925b678ff27ada37075e029f78b5e/definition/ISubscription.ts#L8

View File

@ -38,4 +38,7 @@ export interface IThread extends IMessage {
draftMessage?: string;
}
export type TThreadModel = IThread & Model;
export type TThreadModel = IThread &
Model & {
asPlain: () => IThread;
};

View File

@ -6,4 +6,7 @@ export interface IThreadMessage extends IMessage {
tmsg?: string;
}
export type TThreadMessageModel = IThreadMessage & Model;
export type TThreadMessageModel = IThreadMessage &
Model & {
asPlain: () => IThreadMessage;
};

View File

@ -85,4 +85,47 @@ export default class Message extends Model {
@json('md', sanitizer) md;
@field('comment') comment;
asPlain() {
return {
id: this.id,
rid: this.subscription.id,
msg: this.msg,
t: this.t,
ts: this.ts,
u: this.u,
alias: this.alias,
parseUrls: this.parseUrls,
groupable: this.groupable,
avatar: this.avatar,
emoji: this.emoji,
attachments: this.attachments,
urls: this.urls,
_updatedAt: this._updatedAt,
status: this.status,
pinned: this.pinned,
starred: this.starred,
editedBy: this.editedBy,
reactions: this.reactions,
role: this.role,
drid: this.drid,
dcount: this.dcount,
dlm: this.dlm,
tmid: this.tmid,
tcount: this.tcount,
tlm: this.tlm,
replies: this.replies,
mentions: this.mentions,
channels: this.channels,
unread: this.unread,
autoTranslate: this.autoTranslate,
translations: this.translations,
tmsg: this.tmsg,
blocks: this.blocks,
e2e: this.e2e,
tshow: this.tshow,
md: this.md,
comment: this.comment
};
}
}

View File

@ -136,4 +136,68 @@ export default class Subscription extends Model {
@field('on_hold') onHold;
@json('source', sanitizer) source;
// TODO: if this is proven to be the best way to do it, we should use TS to map through the properties
asPlain() {
return {
_id: this._id,
f: this.f,
t: this.t,
ts: this.ts,
ls: this.ls,
name: this.name,
fname: this.fname,
rid: this.rid,
open: this.open,
alert: this.alert,
unread: this.unread,
userMentions: this.userMentions,
groupMentions: this.groupMentions,
roomUpdatedAt: this.roomUpdatedAt,
ro: this.ro,
lastOpen: this.lastOpen,
description: this.description,
announcement: this.announcement,
bannerClosed: this.bannerClosed,
topic: this.topic,
blocked: this.blocked,
blocker: this.blocker,
reactWhenReadOnly: this.reactWhenReadOnly,
archived: this.archived,
joinCodeRequired: this.joinCodeRequired,
notifications: this.notifications,
broadcast: this.broadcast,
prid: this.prid,
draftMessage: this.draftMessage,
lastThreadSync: this.lastThreadSync,
jitsiTimeout: this.jitsiTimeout,
autoTranslate: this.autoTranslate,
autoTranslateLanguage: this.autoTranslateLanguage,
hideUnreadStatus: this.hideUnreadStatus,
hideMentionStatus: this.hideMentionStatus,
departmentId: this.departmentId,
E2EKey: this.E2EKey,
encrypted: this.encrypted,
e2eKeyId: this.e2eKeyId,
avatarETag: this.avatarETag,
teamId: this.teamId,
teamMain: this.teamMain,
onHold: this.onHold,
roles: this.roles,
tunread: this.tunread,
tunreadUser: this.tunreadUser,
tunreadGroup: this.tunreadGroup,
muted: this.muted,
ignored: this.ignored,
lastMessage: this.lastMessage,
sysMes: this.sysMes,
uids: this.uids,
usernames: this.usernames,
visitor: this.visitor,
servedBy: this.servedBy,
livechatData: this.livechatData,
tags: this.tags,
source: this.source
};
}
}

View File

@ -77,4 +77,42 @@ export default class Thread extends Model {
@field('e2e') e2e;
@field('draft_message') draftMessage;
asPlain() {
return {
id: this.id,
msg: this.msg,
t: this.t,
ts: this.ts,
u: this.u,
alias: this.alias,
parseUrls: this.parseUrls,
groupable: this.groupable,
avatar: this.avatar,
emoji: this.emoji,
attachments: this.attachments,
urls: this.urls,
_updatedAt: this._updatedAt,
status: this.status,
pinned: this.pinned,
starred: this.starred,
editedBy: this.editedBy,
reactions: this.reactions,
role: this.role,
drid: this.drid,
dcount: this.dcount,
dlm: this.dlm,
tmid: this.tmid,
tcount: this.tcount,
tlm: this.tlm,
replies: this.replies,
mentions: this.mentions,
channels: this.channels,
unread: this.unread,
autoTranslate: this.autoTranslate,
translations: this.translations,
e2e: this.e2e,
draftMessage: this.draftMessage
};
}
}

View File

@ -77,4 +77,42 @@ export default class ThreadMessage extends Model {
@field('draft_message') draftMessage;
@field('e2e') e2e;
asPlain() {
return {
id: this.id,
msg: this.msg,
t: this.t,
ts: this.ts,
u: this.u,
rid: this.rid,
alias: this.alias,
parseUrls: this.parseUrls,
groupable: this.groupable,
avatar: this.avatar,
emoji: this.emoji,
attachments: this.attachments,
urls: this.urls,
_updatedAt: this._updatedAt,
status: this.status,
pinned: this.pinned,
starred: this.starred,
editedBy: this.editedBy,
reactions: this.reactions,
role: this.role,
drid: this.drid,
dcount: this.dcount,
dlm: this.dlm,
tcount: this.tcount,
tlm: this.tlm,
replies: this.replies,
mentions: this.mentions,
channels: this.channels,
unread: this.unread,
autoTranslate: this.autoTranslate,
translations: this.translations,
draftMessage: this.draftMessage,
e2e: this.e2e
};
}
}

View File

@ -1,16 +1,15 @@
import log from './helpers/log';
import { TMessageModel, TSubscriptionModel } from '../../definitions';
import { IMessage, TSubscriptionModel } from '../../definitions';
import { store } from '../store/auxStore';
import { isGroupChat } from './helpers';
import { getRoom } from './getRoom';
type TRoomType = 'p' | 'c' | 'd';
export async function getPermalinkMessage(message: TMessageModel): Promise<string | null> {
if (!message.subscription) return null;
export async function getPermalinkMessage(message: IMessage): Promise<string | null> {
let room: TSubscriptionModel;
try {
room = await getRoom(message.subscription.id);
room = await getRoom(message.rid);
} catch (e) {
log(e);
return null;

View File

@ -1,6 +1,7 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Model } from '@nozbe/watermelondb';
import { getMessageById } from '../database/services/Message';
import database from '../database';
import log from './helpers/log';
import { random } from './helpers';
@ -66,14 +67,17 @@ async function sendMessageCall(message: any) {
export async function resendMessage(message: TMessageModel, tmid?: string) {
const db = database.active;
try {
await db.write(async () => {
await message.update(m => {
m.status = messagesStatus.TEMP;
const messageRecord = await getMessageById(message.id);
if (messageRecord) {
await db.write(async () => {
await messageRecord.update(m => {
m.status = messagesStatus.TEMP;
});
});
});
}
const m = await Encryption.encryptMessage({
_id: message.id,
rid: message.subscription ? message.subscription.id : '',
rid: message.rid,
msg: message.msg,
...(tmid && { tmid })
} as IMessage);

View File

@ -14,7 +14,7 @@ import { addUserTyping, clearUserTyping, removeUserTyping } from '../../../actio
import { debounce } from '../helpers';
import { subscribeRoom, unsubscribeRoom } from '../../../actions/room';
import { Encryption } from '../../encryption';
import { IMessage, TMessageModel, TSubscriptionModel, TThreadMessageModel, TThreadModel } from '../../../definitions';
import { IMessage, TMessageModel, TThreadMessageModel, TThreadModel } from '../../../definitions';
import { IDDPMessage } from '../../../definitions/IDDPMessage';
import sdk from '../../services/sdk';
import { readMessages } from '../readMessages';
@ -33,7 +33,7 @@ export default class RoomSubscription {
private _threadsBatch: { [key: string]: TThreadModel };
private threadMessagesBatch: {};
private _threadMessagesBatch: { [key: string]: TThreadMessageModel };
private promises?: Promise<TSubscriptionModel[]>;
private promises?: Promise<any>;
private connectedListener?: Promise<any>;
private disconnectedListener?: Promise<any>;
private notifyRoomListener?: Promise<any>;
@ -79,7 +79,7 @@ export default class RoomSubscription {
if (this.promises) {
try {
const subscriptions = (await this.promises) || [];
subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
subscriptions.forEach((sub: any) => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
} catch (e) {
// do nothing
}

View File

@ -106,6 +106,7 @@ const Drawer = createDrawerNavigator<MasterDetailDrawerParamList>();
const DrawerNavigator = React.memo(() => (
<Drawer.Navigator
screenOptions={{ drawerType: 'permanent', headerShown: false, drawerStyle: { ...drawerStyle } }}
// @ts-ignore
drawerContent={({ navigation, state }) => <RoomsListView navigation={navigation} state={state} />}
>
<Drawer.Screen name='ChatsStackNavigator' component={ChatsStackNavigator} />

View File

@ -5,7 +5,7 @@ import { IItem } from '../views/TeamChannelsView';
import { IOptionsField } from '../views/NotificationPreferencesView/options';
import { IServer } from '../definitions/IServer';
import { IAttachment } from '../definitions/IAttachment';
import { IMessage, TAnyMessageModel, TMessageModel } from '../definitions/IMessage';
import { IMessage, TAnyMessage, TMessageModel } from '../definitions/IMessage';
import { ISubscription, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription';
import { ICannedResponse } from '../definitions/ICannedResponse';
import { TDataSelect } from '../definitions/IDataSelect';
@ -37,7 +37,7 @@ export type ChatsStackParamList = {
roomUserId?: string | null;
usedCannedResponse?: string;
status?: string;
replyInDM?: TAnyMessageModel;
replyInDM?: TAnyMessage;
}
| undefined; // Navigates back to RoomView already on stack
RoomActionsView: {

View File

@ -27,7 +27,7 @@ import {
SubscriptionType,
IAttachment,
IMessage,
TAnyMessageModel,
TAnyMessage,
IUrl,
TGetCustomEmoji,
ICustomEmoji
@ -160,7 +160,7 @@ class MessagesView extends React.Component<IMessagesViewProps, IMessagesViewStat
defineMessagesViewContent = (name: string) => {
const { user, baseUrl, theme, useRealName } = this.props;
const renderItemCommonProps = (item: TAnyMessageModel) => ({
const renderItemCommonProps = (item: TAnyMessage) => ({
item,
baseUrl,
user,
@ -219,7 +219,7 @@ class MessagesView extends React.Component<IMessagesViewProps, IMessagesViewStat
},
noDataMsg: I18n.t('No_mentioned_messages'),
testID: 'mentioned-messages-view',
renderItem: (item: TAnyMessageModel) => <Message {...renderItemCommonProps(item)} msg={item.msg} theme={theme} />
renderItem: (item: TAnyMessage) => <Message {...renderItemCommonProps(item)} msg={item.msg} theme={theme} />
},
// Starred Messages Screen
Starred: {
@ -230,7 +230,7 @@ class MessagesView extends React.Component<IMessagesViewProps, IMessagesViewStat
},
noDataMsg: I18n.t('No_starred_messages'),
testID: 'starred-messages-view',
renderItem: (item: TAnyMessageModel) => (
renderItem: (item: TAnyMessage) => (
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
),
action: (message: IMessage) => ({
@ -249,7 +249,7 @@ class MessagesView extends React.Component<IMessagesViewProps, IMessagesViewStat
},
noDataMsg: I18n.t('No_pinned_messages'),
testID: 'pinned-messages-view',
renderItem: (item: TAnyMessageModel) => (
renderItem: (item: TAnyMessage) => (
<Message {...renderItemCommonProps(item)} msg={item.msg} onLongPress={() => this.onLongPress(item)} theme={theme} />
),
action: () => ({ title: I18n.t('Unpin'), icon: 'pin', onPress: this.handleActionPress }),

View File

@ -1,42 +1,32 @@
import PropTypes from 'prop-types';
import React from 'react';
import { FlatListProps, StyleSheet } from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import { StyleSheet } from 'react-native';
import { FlashList, FlashListProps } from '@shopify/flash-list';
import Animated from 'react-native-reanimated';
import { isIOS } from '../../../lib/methods/helpers';
import scrollPersistTaps from '../../../lib/methods/helpers/scrollPersistTaps';
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const AnimatedFlashList = Animated.createAnimatedComponent(FlashList);
const styles = StyleSheet.create({
list: {
flex: 1
},
contentContainer: {
paddingTop: 10
paddingTop: 8
}
});
export type TListRef = React.RefObject<FlatList & { getNode: () => FlatList }>;
export interface IListProps extends FlatListProps<any> {
listRef: TListRef;
}
export type IListProps = FlashListProps<any>;
// @ts-ignore
const List = ({ listRef, ...props }: IListProps) => (
<AnimatedFlatList
<AnimatedFlashList
testID='room-view-messages'
ref={listRef}
keyExtractor={(item: any) => item.id}
// @ts-ignore
contentContainerStyle={styles.contentContainer}
style={styles.list}
inverted={isIOS}
removeClippedSubviews={isIOS}
initialNumToRender={7}
inverted
estimatedItemSize={48}
onEndReachedThreshold={0.5}
maxToRenderPerBatch={5}
windowSize={10}
{...props}
{...scrollPersistTaps}
/>

View File

@ -1,40 +0,0 @@
import React from 'react';
import { RefreshControl as RNRefreshControl, RefreshControlProps, StyleSheet } from 'react-native';
import { useTheme } from '../../../theme';
import { isAndroid } from '../../../lib/methods/helpers';
const style = StyleSheet.create({
container: {
flex: 1
},
inverted: {
scaleY: -1
}
});
interface IRefreshControl extends RefreshControlProps {
children: React.ReactElement;
}
const RefreshControl = ({ children, onRefresh, refreshing }: IRefreshControl): React.ReactElement => {
const { colors } = useTheme();
if (isAndroid) {
return (
<RNRefreshControl
onRefresh={onRefresh}
refreshing={refreshing}
tintColor={colors.auxiliaryText}
style={[style.container, style.inverted]}
>
{children}
</RNRefreshControl>
);
}
const refreshControl = <RNRefreshControl onRefresh={onRefresh} refreshing={refreshing} tintColor={colors.auxiliaryText} />;
return React.cloneElement(children, { refreshControl });
};
export default RefreshControl;

View File

@ -2,35 +2,28 @@ import { Q } from '@nozbe/watermelondb';
import { dequal } from 'dequal';
import moment from 'moment';
import React from 'react';
import { FlatListProps, View, ViewToken, StyleSheet, Platform } from 'react-native';
import { FlatListProps, ViewToken, RefreshControl } from 'react-native';
import { event, Value } from 'react-native-reanimated';
import { Observable, Subscription } from 'rxjs';
import ActivityIndicator from '../../../containers/ActivityIndicator';
import { TAnyMessageModel, TMessageModel, TThreadMessageModel, TThreadModel } from '../../../definitions';
import { MessageType, TAnyMessage, TMessageModel, TThreadMessageModel, TThreadModel } from '../../../definitions';
import database from '../../../lib/database';
import { compareServerVersion, debounce } from '../../../lib/methods/helpers';
import { animateNextTransition } from '../../../lib/methods/helpers/layoutAnimation';
import { animateNextTransition, compareServerVersion, debounce } from '../../../lib/methods/helpers';
// import { animateNextTransition } from '../../../lib/methods/helpers/layoutAnimation';
import log from '../../../lib/methods/helpers/log';
import EmptyRoom from '../EmptyRoom';
// @ts-ignore
import List, { IListProps, TListRef } from './List';
import NavBottomFAB from './NavBottomFAB';
import { loadMissedMessages, loadThreadMessages } from '../../../lib/methods';
import { Services } from '../../../lib/services';
import RefreshControl from './RefreshControl';
import { MESSAGE_TYPE_ANY_LOAD, themes } from '../../../lib/constants';
import { TMessage } from '../definitions';
import { ThemeContext } from '../../../theme';
const QUERY_SIZE = 50;
const styles = StyleSheet.create({
inverted: {
...Platform.select({
android: {
scaleY: -1
}
})
}
});
const onScroll = ({ y }: { y: Value<number> }) =>
event(
[
@ -60,7 +53,7 @@ export interface IListContainerProps {
}
interface IListContainerState {
messages: TAnyMessageModel[];
messages: TMessage[];
refreshing: boolean;
highlightedMessage: string | null;
}
@ -104,30 +97,6 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
console.timeEnd(`${this.constructor.name} mount`);
}
shouldComponentUpdate(nextProps: IListContainerProps, nextState: IListContainerState) {
const { refreshing, highlightedMessage } = this.state;
const { hideSystemMessages, tunread, ignored, loading } = this.props;
if (loading !== nextProps.loading) {
return true;
}
if (highlightedMessage !== nextState.highlightedMessage) {
return true;
}
if (refreshing !== nextState.refreshing) {
return true;
}
if (!dequal(hideSystemMessages, nextProps.hideSystemMessages)) {
return true;
}
if (!dequal(tunread, nextProps.tunread)) {
return true;
}
if (!dequal(ignored, nextProps.ignored)) {
return true;
}
return false;
}
componentDidUpdate(prevProps: IListContainerProps) {
const { hideSystemMessages } = this.props;
if (!dequal(hideSystemMessages, prevProps.hideSystemMessages)) {
@ -141,7 +110,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
this.unsubscribeFocus();
}
this.clearHighlightedMessageTimeout();
console.countReset(`${this.constructor.name}.render calls`);
console.countReset(`${this.constructor.name}.render: ${this.props.tmid || this.props.rid} calls`);
}
// clears previous highlighted message timeout, if exists
@ -154,7 +123,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
query = async () => {
this.count += QUERY_SIZE;
const { rid, tmid, showMessageInMainThread, serverVersion } = this.props;
const { rid, tmid, showMessageInMainThread, serverVersion, listRef } = this.props;
const db = database.active;
// handle servers with version < 3.0.0
@ -163,6 +132,8 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
hideSystemMessages = [];
}
const columnsToObserve = ['_updated_at', 'status'];
if (tmid) {
try {
this.thread = await db.get('threads').find(tmid);
@ -172,7 +143,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
this.messagesObservable = db
.get('thread_messages')
.query(Q.where('rid', tmid), Q.experimentalSortBy('ts', Q.desc), Q.experimentalSkip(0), Q.experimentalTake(this.count))
.observe();
.observeWithColumns(columnsToObserve);
} else if (rid) {
const whereClause = [
Q.where('rid', rid),
@ -186,14 +157,22 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
this.messagesObservable = db
.get('messages')
.query(...whereClause)
.observe();
.observeWithColumns(columnsToObserve);
}
if (rid) {
this.unsubscribeMessages();
this.messagesSubscription = this.messagesObservable?.subscribe(messages => {
let data = messages.map(m => {
if ((MESSAGE_TYPE_ANY_LOAD as MessageType[]).includes(m.t)) {
return m;
}
return m.asPlain();
});
if (tmid && this.thread) {
messages = [...messages, this.thread];
data = [...messages, this.thread.asPlain()];
}
/**
@ -201,14 +180,21 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
* hide system message is enabled
*/
if (compareServerVersion(serverVersion, 'lowerThan', '3.16.0') || hideSystemMessages.length) {
messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
data = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
}
if (this.mounted) {
this.setState({ messages }, () => this.update());
const { messages: prevMessages } = this.state;
this.setState({ messages: data });
// animates only for new messages
if (this.animated && this.viewableItems?.[0]?.index === 0 && prevMessages[0]?.id !== data[0]?.id) {
listRef.current?.prepareForLayoutAnimationRender();
animateNextTransition();
}
} else {
// @ts-ignore
this.state.messages = messages;
this.state.messages = data;
}
// TODO: move it away from here
this.readThreads();
@ -233,7 +219,13 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
}
}, 300);
onEndReached = () => this.query();
onEndReached = () => {
const { messages } = this.state;
if (messages.length < this.count) {
return;
}
this.query();
};
onRefresh = () =>
this.setState({ refreshing: true }, async () => {
@ -255,20 +247,13 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
this.setState({ refreshing: false });
});
update = () => {
if (this.animated) {
animateNextTransition();
}
this.forceUpdate();
};
unsubscribeMessages = () => {
if (this.messagesSubscription && this.messagesSubscription.unsubscribe) {
this.messagesSubscription.unsubscribe();
}
};
getLastMessage = (): TMessageModel | TThreadMessageModel | null => {
getLastMessage = (): TAnyMessage | null => {
const { messages } = this.state;
if (messages.length > 0) {
return messages[0];
@ -353,7 +338,7 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
renderItem: FlatListProps<any>['renderItem'] = ({ item, index }) => {
const { messages, highlightedMessage } = this.state;
const { renderRow } = this.props;
return <View style={styles.inverted}>{renderRow(item, messages[index + 1], highlightedMessage)}</View>;
return renderRow(item, messages[index + 1], highlightedMessage);
};
onViewableItemsChanged: FlatListProps<any>['onViewableItemsChanged'] = ({ viewableItems }) => {
@ -361,28 +346,37 @@ class ListContainer extends React.Component<IListContainerProps, IListContainerS
};
render() {
console.count(`${this.constructor.name}.render calls`);
const { rid, tmid, listRef } = this.props;
const { messages, refreshing } = this.state;
const { rid, tmid, listRef, loading } = this.props;
const { messages, refreshing, highlightedMessage } = this.state;
console.count(`${this.constructor.name}.render: ${tmid || rid} calls`);
return (
<>
<EmptyRoom rid={rid} length={messages.length} mounted={this.mounted} />
<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh}>
<List
onScroll={this.onScroll}
scrollEventThrottle={16}
listRef={listRef}
data={messages}
renderItem={this.renderItem}
onEndReached={this.onEndReached}
ListFooterComponent={this.renderFooter}
onScrollToIndexFailed={this.handleScrollToIndexFailed}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
/>
</RefreshControl>
<NavBottomFAB y={this.y} onPress={this.jumpToBottom} isThread={!!tmid} />
</>
// FIXME: added context directly so we don't have to touch on withTheme's ref
<ThemeContext.Consumer>
{({ theme }) => (
<>
<EmptyRoom rid={rid} length={messages.length} mounted={this.mounted} />
<List
onScroll={this.onScroll}
scrollEventThrottle={16}
listRef={listRef}
data={messages}
extraData={{ loading, highlightedMessage }}
// @ts-ignore
renderItem={this.renderItem}
onEndReached={this.onEndReached}
ListFooterComponent={this.renderFooter}
onScrollToIndexFailed={this.handleScrollToIndexFailed}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
nativeID={tmid || rid}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} tintColor={themes[theme!].auxiliaryText} />
}
/>
<NavBottomFAB y={this.y} onPress={this.jumpToBottom} isThread={!!tmid} />
</>
)}
</ThemeContext.Consumer>
);
}
}

View File

@ -0,0 +1,3 @@
import { TAnyMessage, TMessageModel } from '../../definitions';
export type TMessage = TAnyMessage | TMessageModel;

View File

@ -69,7 +69,7 @@ import {
ISubscription,
IVisitor,
SubscriptionType,
TAnyMessageModel,
TAnyMessage,
TMessageModel,
TSubscriptionModel,
TThreadModel,
@ -78,7 +78,7 @@ import {
TGetCustomEmoji
} from '../../definitions';
import { E2E_MESSAGE_TYPE, E2E_STATUS, MESSAGE_TYPE_ANY_LOAD, MessageTypeLoad, themes } from '../../lib/constants';
import { TListRef } from './List/List';
// import { TListRef } from './List/List';
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
import {
callJitsi,
@ -101,6 +101,7 @@ import {
import { Services } from '../../lib/services';
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
import { goRoom, TGoRoomItem } from '../../lib/methods/helpers/goRoom';
import { TMessage } from './definitions';
type TStateAttrsUpdate = keyof IRoomViewState;
@ -193,7 +194,7 @@ interface IRoomViewState {
member: any;
lastOpen: Date | null;
reactionsModalVisible: boolean;
selectedMessage?: TAnyMessageModel;
selectedMessage?: TAnyMessage;
canAutoTranslate: boolean;
loading: boolean;
editing: boolean;
@ -204,6 +205,12 @@ interface IRoomViewState {
roomUserId?: string | null;
}
const fetchRoomUpdate = (room: any) =>
roomAttrsUpdate?.reduce((ret: any, attr) => {
ret[attr] = room[attr];
return ret;
}, {});
class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
private rid?: string;
private t?: string;
@ -213,7 +220,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
private messagebox: React.RefObject<MessageBoxType>;
private list: React.RefObject<ListContainerType>;
private joinCode: React.RefObject<IJoinCode>;
private flatList: TListRef;
private flatList: any;
private mounted: boolean;
private offset = 0;
private subObserveQuery?: Subscription;
@ -225,7 +232,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
private retryFindTimeout?: ReturnType<typeof setTimeout>;
private messageErrorActions?: IMessageErrorActions | null;
private messageActions?: IMessageActions | null;
private replyInDM?: TAnyMessageModel;
private replyInDM?: TAnyMessage;
// Type of InteractionManager.runAfterInteractions
private didMountInteraction?: {
then: (onfulfilled?: (() => any) | undefined, onrejected?: (() => any) | undefined) => Promise<any>;
@ -264,7 +271,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.state = {
joined: true,
room,
roomUpdate: {},
roomUpdate: fetchRoomUpdate(room),
member: {},
lastOpen: null,
reactionsModalVisible: false,
@ -286,7 +293,8 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.setHeader();
if ('id' in room) {
// TODO: Since we won't be using observables directly anymore, should we remove this?
if ('observe' in room) {
// @ts-ignore TODO: type guard isn't helping here :(
this.observeRoom(room);
} else if (this.rid) {
@ -407,7 +415,13 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
this.setHeader();
}
this.setReadOnly();
if (
!dequal(prevState.roomUpdate.muted, roomUpdate.muted) ||
prevState.roomUpdate.archived !== roomUpdate.archived ||
prevState.roomUpdate.ro !== roomUpdate.ro
) {
this.setReadOnly();
}
}
updateOmnichannel = async () => {
@ -473,7 +487,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
EventEmitter.removeListener(KEY_COMMAND, this.handleCommands);
}
EventEmitter.removeListener('ROOM_REMOVED', this.handleRoomRemoved);
console.countReset(`${this.constructor.name}.render calls`);
console.countReset(`${this.constructor.name}.render: ${this.tmid || room.rid} calls`);
}
canForwardGuest = async () => {
@ -765,10 +779,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
observeRoom = (room: TSubscriptionModel) => {
const observable = room.observe();
this.subSubscription = observable.subscribe(changes => {
const roomUpdate = roomAttrsUpdate.reduce((ret: any, attr) => {
ret[attr] = changes[attr];
return ret;
}, {});
const roomUpdate = fetchRoomUpdate(changes);
if (this.mounted) {
this.internalSetState({ room: changes, roomUpdate, isOnHold: !!changes?.onHold });
} else {
@ -780,7 +791,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
});
};
errorActionsShow = (message: TAnyMessageModel) => {
errorActionsShow = (message: TAnyMessage) => {
this.messagebox?.current?.closeEmojiAndAction(this.messageErrorActions?.showMessageErrorActions, message);
};
@ -789,12 +800,11 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.messagebox?.current?.closeEmojiAndAction(showActionSheet, options);
};
onEditInit = (message: TAnyMessageModel) => {
onEditInit = (message: TAnyMessage) => {
const newMessage = {
id: message.id,
subscription: {
// @ts-ignore TODO: we can remove this after we merge a PR separating IMessage vs IMessageFromServer
id: message.subscription.id
id: message.rid
},
msg: message?.attachments?.[0]?.description || message.msg
} as TMessageModel;
@ -805,7 +815,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.setState({ selectedMessage: undefined, editing: false });
};
onEditRequest = async (message: TAnyMessageModel) => {
onEditRequest = async (message: TAnyMessage) => {
this.setState({ selectedMessage: undefined, editing: false });
try {
await Services.editMessage(message);
@ -814,7 +824,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
onReplyInit = (message: TAnyMessageModel, mention: boolean) => {
onReplyInit = (message: TAnyMessage, mention: boolean) => {
// If there's a thread already, we redirect to it
if (mention && !!message.tlm) {
return this.onThreadPress(message);
@ -844,7 +854,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}, 100);
};
onReactionInit = (message: TAnyMessageModel) => {
onReactionInit = (message: TAnyMessage) => {
this.messagebox?.current?.closeEmojiAndAction(() => {
this.setState({ selectedMessage: message }, this.showReactionPicker);
});
@ -855,7 +865,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
this.setState({ selectedMessage: undefined }, hideActionSheet);
};
onMessageLongPress = (message: TAnyMessageModel) => {
onMessageLongPress = (message: TAnyMessage) => {
// if it's a thread message on main room, we disable the long press
if (message.tmid && !this.tmid) {
return;
@ -885,7 +895,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
onReactionLongPress = (message: TAnyMessageModel) => {
onReactionLongPress = (message: TAnyMessage) => {
this.setState({ selectedMessage: message });
const { showActionSheet } = this.props;
const { selectedMessage } = this.state;
@ -911,7 +921,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
};
onDiscussionPress = debounce(
async (item: TAnyMessageModel) => {
async (item: TAnyMessage) => {
const { isMasterDetail } = this.props;
if (!item.drid) return;
const sub = await getRoomInfo(item.drid);
@ -946,7 +956,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
});
};
onThreadPress = debounce((item: TAnyMessageModel) => this.navToThread(item), 1000, true);
onThreadPress = debounce((item: TAnyMessage) => this.navToThread(item), 1000, true);
shouldNavigateToRoom = (message: IMessage) => {
if (message.tmid && message.tmid === this.tmid) {
@ -1048,9 +1058,9 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
const { rid } = this.state.room;
const { user } = this.props;
sendMessage(rid, message, this.tmid || tmid, user, tshow).then(() => {
if (this.list && this.list.current) {
this.list.current?.update();
}
// if (this.list && this.list.current) {
// this.list.current?.update();
// }
this.setLastOpen(null);
Review.pushPositiveEvent();
});
@ -1155,7 +1165,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
navToThread = async (item: TAnyMessageModel | { tmid: string }) => {
navToThread = async (item: TAnyMessage | { tmid: string }) => {
const { roomUserId } = this.state;
const { navigation } = this.props;
@ -1204,7 +1214,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
navToRoom = async (message: TAnyMessageModel) => {
navToRoom = async (message: TAnyMessage) => {
const { isMasterDetail } = this.props;
const roomInfo = await getRoomInfo(message.rid);
@ -1293,7 +1303,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
}
};
isIgnored = (message: TAnyMessageModel): boolean => {
isIgnored = (message: TAnyMessage): boolean => {
const { room } = this.state;
if ('id' in room) {
return room?.ignored?.includes?.(message?.u?._id) ?? false;
@ -1301,7 +1311,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
return false;
};
onLoadMoreMessages = (loaderItem: TAnyMessageModel) => {
onLoadMoreMessages = (loaderItem: TMessageModel) => {
const { room } = this.state;
return RoomServices.getMoreMessages({
rid: room.rid,
@ -1316,7 +1326,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
Navigation.navigate('CannedResponsesListView', { rid: room.rid });
};
renderItem = (item: TAnyMessageModel, previousItem: TAnyMessageModel, highlightedMessage?: string) => {
renderItem = (item: TMessage, previousItem: TMessage, highlightedMessage?: string) => {
const { room, lastOpen, canAutoTranslate } = this.state;
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
this.props;
@ -1337,7 +1347,7 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
if (item.t && MESSAGE_TYPE_ANY_LOAD.includes(item.t as MessageTypeLoad)) {
content = (
<LoadMore
load={() => this.onLoadMoreMessages(item)}
load={() => this.onLoadMoreMessages(item as TMessageModel)}
type={item.t}
runOnRender={item.t === MessageTypeLoad.MORE && !previousItem}
/>
@ -1518,10 +1528,10 @@ class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
};
render() {
console.count(`${this.constructor.name}.render calls`);
const { room, loading } = this.state;
const { user, baseUrl, theme, navigation, Hide_System_Messages, width, serverVersion } = this.props;
const { rid, t } = room;
console.count(`${this.constructor.name}.render: ${this.tmid || rid} calls`);
let sysMes;
let bannerClosed;
let announcement;

View File

@ -1,4 +1,4 @@
import { SubscriptionType, TAnyMessageModel } from '../../../definitions';
import { SubscriptionType, TMessageModel } from '../../../definitions';
import { loadNextMessages, loadMessagesForRoom } from '../../../lib/methods';
import { MessageTypeLoad } from '../../../lib/constants';
@ -11,7 +11,7 @@ const getMoreMessages = ({
rid: string;
t: SubscriptionType;
tmid?: string;
loaderItem: TAnyMessageModel;
loaderItem: TMessageModel;
}): Promise<void> => {
if ([MessageTypeLoad.MORE, MessageTypeLoad.PREVIOUS_CHUNK].includes(loaderItem.t as MessageTypeLoad)) {
return loadMessagesForRoom({

View File

@ -1,13 +1,13 @@
import React from 'react';
import { BackHandler, FlatList, Keyboard, NativeEventSubscription, RefreshControl, Text, View } from 'react-native';
import { BackHandler, Keyboard, NativeEventSubscription, RefreshControl, Text, View } from 'react-native';
import { batch, connect } from 'react-redux';
import { dequal } from 'dequal';
import Orientation from 'react-native-orientation-locker';
import { Q } from '@nozbe/watermelondb';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
import { Subscription } from 'rxjs';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { Header } from '@react-navigation/elements';
import { FlashList } from '@shopify/flash-list';
import { CompositeNavigationProp, RouteProp } from '@react-navigation/native';
import { Dispatch } from 'redux';
@ -39,7 +39,6 @@ import {
import { getUserSelector } from '../../selectors/login';
import { goRoom } from '../../lib/methods/helpers/goRoom';
import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions';
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
import { IApplicationState, ISubscription, IUser, RootEnum, SubscriptionType, TSubscriptionModel } from '../../definitions';
import styles from './styles';
@ -55,10 +54,9 @@ import {
hasPermission,
isRead,
debounce,
isIOS,
isTablet
} from '../../lib/methods/helpers';
import { E2E_BANNER_TYPE, DisplayMode, SortBy, MAX_SIDEBAR_WIDTH, themes } from '../../lib/constants';
import { E2E_BANNER_TYPE, DisplayMode, SortBy, themes } from '../../lib/constants';
import { Services } from '../../lib/services';
type TNavigation = CompositeNavigationProp<
@ -71,7 +69,6 @@ interface IRoomsListViewProps {
route: RouteProp<ChatsStackParamList, 'RoomsListView'>;
theme: TSupportedThemes;
dispatch: Dispatch;
[key: string]: IUser | string | boolean | ISubscription[] | number | object | TEncryptionBanner;
user: IUser;
server: string;
searchText: string;
@ -87,29 +84,22 @@ interface IRoomsListViewProps {
useRealName: boolean;
isMasterDetail: boolean;
subscribedRoom: string;
width: number;
insets: {
left: number;
right: number;
};
queueSize: number;
inquiryEnabled: boolean;
encryptionBanner: TEncryptionBanner;
encryptionBanner: string;
showAvatar: boolean;
displayMode: string;
createTeamPermission: [];
createDirectMessagePermission: [];
createPublicChannelPermission: [];
createPrivateChannelPermission: [];
createDiscussionPermission: [];
createTeamPermission?: string[];
createDirectMessagePermission?: string[];
createPublicChannelPermission?: string[];
createPrivateChannelPermission?: string[];
createDiscussionPermission?: string[];
}
interface IRoomsListViewState {
searching?: boolean;
search?: IRoomItem[];
loading?: boolean;
chatsUpdate?: string[] | { rid: string; alert?: boolean }[];
omnichannelsUpdate?: string[];
chats?: IRoomItem[];
item?: ISubscription;
canCreateRoom?: boolean;
@ -120,7 +110,6 @@ interface IRoomItem extends ISubscription {
outside?: boolean;
}
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
const CHATS_HEADER = 'Chats';
const UNREAD_HEADER = 'Unread';
const FAVORITES_HEADER = 'Favorites';
@ -132,53 +121,22 @@ const OMNICHANNEL_HEADER_IN_PROGRESS = 'Open_Livechats';
const OMNICHANNEL_HEADER_ON_HOLD = 'On_hold_Livechats';
const QUERY_SIZE = 20;
const filterIsUnread = (s: TSubscriptionModel) => (s.unread > 0 || s.tunread?.length > 0 || s.alert) && !s.hideUnreadStatus;
const filterIsFavorite = (s: TSubscriptionModel) => s.f;
const filterIsOmnichannel = (s: TSubscriptionModel) => s.t === 'l';
const filterIsTeam = (s: TSubscriptionModel) => s.teamMain;
const filterIsDiscussion = (s: TSubscriptionModel) => s.prid;
const filterIsUnread = (s: any) => (s.unread > 0 || s.tunread?.length > 0 || s.alert) && !s.hideUnreadStatus;
const filterIsFavorite = (s: any) => s.f;
const filterIsOmnichannel = (s: any) => s.t === 'l';
const filterIsTeam = (s: any) => s.teamMain;
const filterIsDiscussion = (s: any) => s.prid;
const shouldUpdateProps = [
'searchText',
'loadingServer',
'showServerDropdown',
'useRealName',
'StoreLastMessage',
'theme',
'isMasterDetail',
'refreshing',
'queueSize',
'inquiryEnabled',
'encryptionBanner',
'createTeamPermission',
'createDirectMessagePermission',
'createPublicChannelPermission',
'createPrivateChannelPermission',
'createDiscussionPermission'
];
const sortPreferencesShouldUpdate = ['sortBy', 'groupByType', 'showFavorites', 'showUnread'];
const displayPropsShouldUpdate = ['showAvatar', 'displayMode'];
const getItemLayout = (data: ISubscription[] | null | undefined, index: number, height: number) => ({
length: height,
offset: height * index,
index
});
const keyExtractor = (item: ISubscription) => item.rid;
class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewState> {
private animated: boolean;
private mounted: boolean;
private count: number;
private unsubscribeFocus?: () => void;
private unsubscribeBlur?: () => void;
private sortPreferencesChanged?: boolean;
private shouldUpdate?: boolean;
private backHandler?: NativeEventSubscription;
private querySubscription?: Subscription;
private scroll?: FlatList;
private scroll?: any;
private useRealName?: boolean;
constructor(props: IRoomsListViewProps) {
@ -187,14 +145,11 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
console.time(`${this.constructor.name} mount`);
this.animated = false;
this.mounted = false;
this.count = 0;
this.state = {
searching: false,
search: [],
loading: true,
chatsUpdate: [] as TSubscriptionModel[],
omnichannelsUpdate: [],
chats: [],
item: {} as ISubscription,
canCreateRoom: false
@ -206,7 +161,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
componentDidMount() {
const { navigation, dispatch } = this.props;
this.handleHasPermission();
this.mounted = true;
if (isTablet) {
EventEmitter.addEventListener(KEY_COMMAND, this.handleCommands);
@ -214,16 +168,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
this.unsubscribeFocus = navigation.addListener('focus', () => {
Orientation.unlockAllOrientations();
this.animated = true;
// Check if there were changes with sort preference, then call getSubscription to remount the list
if (this.sortPreferencesChanged) {
this.getSubscriptions();
this.sortPreferencesChanged = false;
}
// Check if there were changes while not focused (it's set on sCU)
if (this.shouldUpdate) {
this.forceUpdate();
this.shouldUpdate = false;
}
this.backHandler = BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
});
this.unsubscribeBlur = navigation.addListener('blur', () => {
@ -253,81 +197,61 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
}
}
shouldComponentUpdate(nextProps: IRoomsListViewProps, nextState: IRoomsListViewState) {
const { chatsUpdate, searching, item, canCreateRoom, omnichannelsUpdate } = this.state;
// eslint-disable-next-line react/destructuring-assignment
const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]);
if (propsUpdated) {
shouldComponentUpdate(nextProps: Readonly<IRoomsListViewProps>, nextState: Readonly<IRoomsListViewState>): boolean {
const {
createTeamPermission,
createPublicChannelPermission,
createPrivateChannelPermission,
createDirectMessagePermission,
createDiscussionPermission
} = this.props;
if (
!dequal(createTeamPermission, nextProps.createTeamPermission) ||
!dequal(createPublicChannelPermission, nextProps.createPublicChannelPermission) ||
!dequal(createPrivateChannelPermission, nextProps.createPrivateChannelPermission) ||
!dequal(createDirectMessagePermission, nextProps.createDirectMessagePermission) ||
!dequal(createDiscussionPermission, nextProps.createDiscussionPermission)
) {
return true;
}
// check if some display props are changed to force update when focus this view again
// eslint-disable-next-line react/destructuring-assignment
const displayUpdated = displayPropsShouldUpdate.some(key => nextProps[key] !== this.props[key]);
if (displayUpdated) {
this.shouldUpdate = true;
}
// check if some sort preferences are changed to getSubscription() when focus this view again
// eslint-disable-next-line react/destructuring-assignment
const sortPreferencesUpdate = sortPreferencesShouldUpdate.some(key => nextProps[key] !== this.props[key]);
if (sortPreferencesUpdate) {
this.sortPreferencesChanged = true;
}
// Compare changes only once
const chatsNotEqual = !dequal(nextState.chatsUpdate, chatsUpdate);
// If they aren't equal, set to update if focused
if (chatsNotEqual) {
this.shouldUpdate = true;
}
const omnichannelsNotEqual = !dequal(nextState.omnichannelsUpdate, omnichannelsUpdate);
if (omnichannelsNotEqual) {
this.shouldUpdate = true;
}
if (nextState.searching !== searching) {
const { chats, search } = this.state;
if (!dequal(chats, nextState.chats) || !dequal(search, nextState.search)) {
return true;
}
if (nextState.canCreateRoom !== canCreateRoom) {
const {
sortBy,
groupByType,
showFavorites,
showUnread,
subscribedRoom,
isMasterDetail,
showAvatar,
displayMode,
encryptionBanner,
showServerDropdown
} = this.props;
if (
sortBy !== nextProps.sortBy ||
groupByType !== nextProps.groupByType ||
showFavorites !== nextProps.showFavorites ||
showUnread !== nextProps.showUnread ||
showAvatar !== nextProps.showAvatar ||
displayMode !== nextProps.displayMode ||
(subscribedRoom !== nextProps.subscribedRoom && isMasterDetail) ||
isMasterDetail !== nextProps.isMasterDetail ||
encryptionBanner !== nextProps.encryptionBanner ||
showServerDropdown !== nextProps.showServerDropdown
) {
return true;
}
if (nextState.item?.rid !== item?.rid) {
const { searching, loading, canCreateRoom } = this.state;
if (searching !== nextState.searching || loading !== nextState.loading || canCreateRoom !== nextState.canCreateRoom) {
return true;
}
// Abort if it's not focused
if (!nextProps.navigation.isFocused()) {
return false;
}
const { loading, search } = this.state;
const { width, insets, subscribedRoom } = this.props;
if (nextState.loading !== loading) {
return true;
}
if (nextProps.width !== width) {
return true;
}
if (!dequal(nextState.search, search)) {
return true;
}
if (nextProps.subscribedRoom !== subscribedRoom) {
return true;
}
if (!dequal(nextProps.insets, insets)) {
return true;
}
// If it's focused and there are changes, update
if (chatsNotEqual || omnichannelsNotEqual) {
this.shouldUpdate = false;
return true;
}
return false;
}
@ -339,7 +263,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
showUnread,
subscribedRoom,
isMasterDetail,
insets,
createTeamPermission,
createPublicChannelPermission,
createPrivateChannelPermission,
@ -366,9 +289,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
if (isMasterDetail && item?.rid !== subscribedRoom && subscribedRoom !== prevProps.subscribedRoom) {
this.setState({ item: { rid: subscribedRoom } as ISubscription });
}
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
this.setHeader();
}
if (
!dequal(createTeamPermission, prevProps.createTeamPermission) ||
@ -481,13 +401,14 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
| (Pick<IRoomsListViewState, keyof IRoomsListViewState> | IRoomsListViewState | null),
callback?: () => void
) => {
this.setState(state, callback);
if (this.animated) {
this.scroll?.prepareForLayoutAnimationRender();
animateNextTransition();
}
this.setState(state, callback);
};
addRoomsGroup = (data: TSubscriptionModel[], header: string, allData: TSubscriptionModel[]) => {
addRoomsGroup = (data: ISubscription[], header: string, allData: ISubscription[]) => {
if (data.length > 0) {
if (header) {
allData.push({ rid: header, separator: true } as TSubscriptionModel);
@ -498,6 +419,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
};
getSubscriptions = async () => {
console.log('🚀 ~ file: index.tsx ~ line 408 ~ RoomsListView ~ getSubscriptions');
this.unsubscribeQuery();
const { sortBy, showUnread, showFavorites, groupByType, user } = this.props;
@ -506,6 +428,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
let observable;
const defaultWhereClause = [Q.where('archived', false), Q.where('open', true)] as (Q.WhereDescription | Q.SortBy)[];
const columnsToObserve = ['alert', 'f', 'on_hold', 'room_updated_at'];
if (sortBy === SortBy.Alphabetical) {
defaultWhereClause.push(Q.experimentalSortBy(`${this.useRealName ? 'fname' : 'name'}`, Q.asc));
@ -518,28 +441,26 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
observable = await db
.get('subscriptions')
.query(...defaultWhereClause)
.observeWithColumns(['alert', 'on_hold']);
.observeWithColumns(columnsToObserve);
// When we're NOT grouping
} else {
this.count += QUERY_SIZE;
observable = await db
.get('subscriptions')
.query(...defaultWhereClause, Q.experimentalSkip(0), Q.experimentalTake(this.count))
.observeWithColumns(['on_hold']);
.observeWithColumns(columnsToObserve);
}
this.querySubscription = observable.subscribe(data => {
let tempChats = [] as TSubscriptionModel[];
let chats = data;
let tempChats: ISubscription[] = [];
let chats = data.map(item => item.asPlain());
let omnichannelsUpdate: string[] = [];
const isOmnichannelAgent = user?.roles?.includes('livechat-agent');
if (isOmnichannelAgent) {
const omnichannel = chats.filter(s => filterIsOmnichannel(s));
const omnichannelInProgress = omnichannel.filter(s => !s.onHold);
const omnichannelOnHold = omnichannel.filter(s => s.onHold);
chats = chats.filter(s => !filterIsOmnichannel(s));
omnichannelsUpdate = omnichannelInProgress.map(s => s.rid);
tempChats = this.addRoomsGroup(omnichannelInProgress, OMNICHANNEL_HEADER_IN_PROGRESS, tempChats);
tempChats = this.addRoomsGroup(omnichannelOnHold, OMNICHANNEL_HEADER_ON_HOLD, tempChats);
}
@ -574,12 +495,8 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
tempChats = chats;
}
const chatsUpdate = tempChats.map(item => item.rid);
this.internalSetState({
chats: tempChats,
chatsUpdate,
omnichannelsUpdate,
loading: false
});
});
@ -893,13 +810,14 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
};
onEndReached = () => {
// Run only when we're not grouping by anything
if (!this.isGrouping) {
this.getSubscriptions();
const { searching } = this.state;
if (searching) {
return;
}
this.getSubscriptions();
};
getScrollRef = (ref: FlatList) => (this.scroll = ref);
getScrollRef = (ref: any) => (this.scroll = ref);
renderListHeader = () => {
const { searching } = this.state;
@ -911,7 +829,7 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
goQueue={this.goQueue}
queueSize={queueSize}
inquiryEnabled={inquiryEnabled}
encryptionBanner={encryptionBanner}
encryptionBanner={encryptionBanner as TEncryptionBanner}
user={user}
/>
);
@ -938,8 +856,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
user: { username },
StoreLastMessage,
useRealName,
isMasterDetail,
width,
showAvatar,
displayMode
} = this.props;
@ -953,7 +869,6 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
username={username}
showLastMessage={StoreLastMessage}
onPress={this.onPressItem}
width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width}
toggleFav={this.toggleFav}
toggleRead={this.toggleRead}
hideChannel={this.hideChannel}
@ -978,48 +893,42 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
);
};
renderScroll = () => {
const { loading, chats, search, searching } = this.state;
const { theme, refreshing, displayMode } = this.props;
const height = displayMode === DisplayMode.Condensed ? ROW_HEIGHT_CONDENSED : ROW_HEIGHT;
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
ref={this.getScrollRef}
data={searching ? search : chats}
extraData={searching ? search : chats}
keyExtractor={keyExtractor}
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
renderItem={this.renderItem}
ListHeaderComponent={this.renderListHeader}
getItemLayout={(data, index) => getItemLayout(data, index, height)}
removeClippedSubviews={isIOS}
keyboardShouldPersistTaps='always'
initialNumToRender={INITIAL_NUM_TO_RENDER}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} tintColor={themes[theme].auxiliaryText} />
}
windowSize={9}
onEndReached={this.onEndReached}
onEndReachedThreshold={0.5}
/>
);
};
render = () => {
console.count(`${this.constructor.name}.render calls`);
const { showServerDropdown, theme, navigation } = this.props;
const { showServerDropdown, theme, navigation, refreshing, displayMode, queueSize, inquiryEnabled, encryptionBanner } =
this.props;
const { loading, chats, search, searching } = this.state;
const height = displayMode === DisplayMode.Condensed ? ROW_HEIGHT_CONDENSED : ROW_HEIGHT;
return (
<SafeAreaView testID='rooms-list-view' style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar />
{this.renderHeader()}
{this.renderScroll()}
{loading ? (
<ActivityIndicator />
) : (
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
<FlashList
ref={this.getScrollRef}
data={searching ? search : chats}
extraData={{ theme, queueSize, inquiryEnabled, encryptionBanner }}
keyExtractor={keyExtractor}
renderItem={this.renderItem}
getItemType={item => (item.separator ? 'section' : 'item')}
ListHeaderComponent={this.renderListHeader}
estimatedItemSize={height}
keyboardShouldPersistTaps='always'
refreshControl={
searching ? undefined : (
<RefreshControl refreshing={refreshing} onRefresh={this.onRefresh} tintColor={themes[theme].auxiliaryText} />
)
}
onEndReached={this.isGrouping ? undefined : this.onEndReached}
onEndReachedThreshold={this.isGrouping ? undefined : 0.5}
/>
</View>
)}
{/* TODO - this ts-ignore is here because the route props, on IBaseScreen*/}
{/* @ts-ignore*/}
{showServerDropdown ? <ServerDropdown navigation={navigation} theme={theme} /> : null}
@ -1041,8 +950,8 @@ const mapStateToProps = (state: IApplicationState) => ({
groupByType: state.sortPreferences.groupByType,
showFavorites: state.sortPreferences.showFavorites,
showUnread: state.sortPreferences.showUnread,
useRealName: state.settings.UI_Use_Real_Name,
StoreLastMessage: state.settings.Store_Last_Message,
useRealName: state.settings.UI_Use_Real_Name as boolean,
StoreLastMessage: state.settings.Store_Last_Message as boolean,
subscribedRoom: state.room.subscribedRoom,
queueSize: getInquiryQueueSelector(state).length,
inquiryEnabled: state.inquiry.enabled,
@ -1056,4 +965,4 @@ const mapStateToProps = (state: IApplicationState) => ({
createDiscussionPermission: state.permissions['start-discussion']
});
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
export default connect(mapStateToProps)(withTheme(RoomsListView));

View File

@ -6,9 +6,6 @@ export default StyleSheet.create({
container: {
flex: 1
},
list: {
width: '100%'
},
dropdownContainerHeader: {
height: 41,
borderBottomWidth: StyleSheet.hairlineWidth,

View File

@ -340,7 +340,9 @@ describe('E2E Encryption', () => {
});
it('should add server and create new user', async () => {
await sleep(5000);
await waitFor(element(by.id('rooms-list-header-server-dropdown-button')))
.toBeVisible()
.withTimeout(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown')))
.toBeVisible()

View File

@ -1,14 +1,11 @@
import { expect } from 'detox';
import { navigateToRegister, platformTypes, TTextMatcher } from '../../helpers/app';
import { navigateToRegister } from '../../helpers/app';
import data from '../../data';
describe('Create user screen', () => {
let alertButtonType: string;
let textMatcher: TTextMatcher;
before(async () => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
({ alertButtonType, textMatcher } = platformTypes[device.getPlatform()]);
await navigateToRegister();
});
@ -65,17 +62,17 @@ describe('Create user screen', () => {
// await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
// });
it('should submit username already taken and raise error', async () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.users.existing.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by[textMatcher]('Username is already in use')).atIndex(0))
.toExist()
.withTimeout(10000);
await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
});
// it('should submit username already taken and raise error', async () => {
// await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
// await element(by.id('register-view-username')).replaceText(data.users.existing.username);
// await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
// await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
// await element(by.id('register-view-submit')).tap();
// await waitFor(element(by[textMatcher]('Username is already in use')).atIndex(0))
// .toExist()
// .withTimeout(10000);
// await element(by[textMatcher]('OK').and(by.type(alertButtonType))).tap();
// });
it('should register', async () => {
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);

View File

@ -187,12 +187,19 @@ describe('Room', () => {
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('204')))
.toExist()
.withTimeout(5000);
await waitFor(element(by[textMatcher]('Load Newer')))
.toExist()
.withTimeout(5000);
await element(by[textMatcher]('Load Newer')).atIndex(0).tap();
await waitFor(element(by[textMatcher]('Load Newer')))
.toNotExist()
.withTimeout(5000);
await expect(element(by[textMatcher]('Load More'))).toNotExist();
await expect(element(by[textMatcher]('201'))).toExist();
await expect(element(by[textMatcher]('202'))).toExist();
await expect(element(by[textMatcher]('253'))).toExist();
await expect(element(by[textMatcher]('252'))).toExist();
await tapBack();
});
});

View File

@ -1,11 +1,16 @@
import 'react-native-gesture-handler';
import 'react-native-console-time-polyfill';
import { AppRegistry } from 'react-native';
import { connectToDevTools } from 'react-devtools-core';
import { name as appName, share as shareName } from './app.json';
if (__DEV__) {
require('./app/ReactotronConfig');
connectToDevTools({
host: 'localhost',
port: 8097
});
} else {
console.log = () => {};
console.time = () => {};

View File

@ -364,6 +364,11 @@ PODS:
- React-Core
- react-native-document-picker (8.1.2):
- React-Core
- react-native-flipper-performance-plugin (0.3.1):
- React-Core
- react-native-flipper-performance-plugin/FBDefines (= 0.3.1)
- react-native-flipper-performance-plugin/FBDefines (0.3.1):
- React-Core
- react-native-jitsi-meet (3.6.0):
- JitsiMeetSDK (= 3.6.0)
- React
@ -522,6 +527,8 @@ PODS:
- RNFBApp
- RNFileViewer (2.1.5):
- React-Core
- RNFlashList (1.2.2):
- React-Core
- RNGestureHandler (2.4.2):
- React-Core
- RNImageCropPicker (0.36.3):
@ -629,6 +636,7 @@ DEPENDENCIES:
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
- "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)"
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-flipper-performance-plugin (from `../node_modules/react-native-flipper-performance-plugin`)
- react-native-jitsi-meet (from `../node_modules/react-native-jitsi-meet`)
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@ -668,6 +676,7 @@ DEPENDENCIES:
- "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
- "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)"
- RNFileViewer (from `../node_modules/react-native-file-viewer`)
- "RNFlashList (from `../node_modules/@shopify/flash-list`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNLocalize (from `../node_modules/react-native-localize`)
@ -783,6 +792,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-cookies/cookies"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-flipper-performance-plugin:
:path: "../node_modules/react-native-flipper-performance-plugin"
react-native-jitsi-meet:
:path: "../node_modules/react-native-jitsi-meet"
react-native-mmkv-storage:
@ -861,6 +872,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-firebase/crashlytics"
RNFileViewer:
:path: "../node_modules/react-native-file-viewer"
RNFlashList:
:path: "../node_modules/@shopify/flash-list"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNImageCropPicker:
@ -949,6 +962,7 @@ SPEC CHECKSUMS:
react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866
react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c
react-native-document-picker: f5ec1a712ca2a975c233117f044817bb8393cad4
react-native-flipper-performance-plugin: 2b873b68da3e368afeaf29c9c7a8c2b0ff908c4f
react-native-jitsi-meet: 3e3ac5d0445091154119f94342efd55c8b1124ce
react-native-mmkv-storage: 8ba3c0216a6df283ece11205b442a3e435aec4e5
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
@ -988,6 +1002,7 @@ SPEC CHECKSUMS:
RNFBApp: b1b5a80a676a07dea17e778bda7c1e8b69b2f5ec
RNFBCrashlytics: 357955a1564721ca9001960e57b395c6a319f9be
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFlashList: 13d14d9502661134ad3ba892f81d76bdcbd79755
RNGestureHandler: 61628a2c859172551aa2100d3e73d1e57878392f
RNImageCropPicker: 97289cd94fb01ab79db4e5c92938be4d0d63415d
RNLocalize: 82a569022724d35461e2dc5b5d015a13c3ca995b

View File

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
0C6E2DE448364EA896869ADF /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B37C79D9BD0742CE936B6982 /* libc++.tbd */; };
0DAF353368B2DE2714B6DCE8 /* libPods-defaults-Rocket.Chat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F1EEB258E879574E6F9EADA /* libPods-defaults-Rocket.Chat.a */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
1E01C81C2511208400FEF824 /* URL+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E01C81B2511208400FEF824 /* URL+Extensions.swift */; };
@ -80,9 +79,8 @@
1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFEB5972493B6640072EDC0 /* NotificationService.swift */; };
1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 06BB44DD4855498082A744AD /* libz.tbd */; };
4BC950FF98C56CFA992BBAFE /* libPods-defaults-ShareRocketChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3982B430BE28C2D93FD9AF5C /* libPods-defaults-ShareRocketChatRN.a */; };
4C4C8603EF082F0A33A95522 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D5C142B655F8EFD006792C /* ExpoModulesProvider.swift */; };
58B1112437C7F012923203ED /* libPods-defaults-RocketChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 674E4FB148AE2FB17415683F /* libPods-defaults-RocketChatRN.a */; };
77BB45FF3B406BBA2A3C36F9 /* libPods-defaults-NotificationService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 10533AE1624775EF2CE700C5 /* libPods-defaults-NotificationService.a */; };
7A006F14229C83B600803143 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A006F13229C83B600803143 /* GoogleService-Info.plist */; };
7A0D62D2242AB187006D5C06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A0D62D1242AB187006D5C06 /* LaunchScreen.storyboard */; };
7A14FCED257FEB3A005BDCD4 /* Experimental.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A14FCEC257FEB3A005BDCD4 /* Experimental.xcassets */; };
@ -143,10 +141,12 @@
7AE10C0728A59530003593CB /* Inter.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7AE10C0528A59530003593CB /* Inter.ttf */; };
7AE10C0828A59530003593CB /* Inter.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7AE10C0528A59530003593CB /* Inter.ttf */; };
85160EB6C143E0493FE5F014 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194D9A8897F4A486C2C6F89A /* ExpoModulesProvider.swift */; };
A965681B9D9B1DB968676F54 /* libPods-defaults-NotificationService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DEC0A8A825375FCCDCDFEFE2 /* libPods-defaults-NotificationService.a */; };
A891D4519F3C3C0DF281382E /* libPods-defaults-RocketChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA66A470302CC036893877E3 /* libPods-defaults-RocketChatRN.a */; };
BC404914E86821389EEB543D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */; };
D344DEDBA103E81285F48C9F /* libPods-defaults-Rocket.Chat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A9622AB95BAF321135704F /* libPods-defaults-Rocket.Chat.a */; };
D94D81FB9E10756FAA03F203 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 016747EF3B9FED8DE2C9DA14 /* ExpoModulesProvider.swift */; };
DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA7E862283664608B3894E34 /* libWatermelonDB.a */; };
E5BA08EDF1C874FF35785B16 /* libPods-defaults-ShareRocketChatRN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 421FD6D28D6DECEEC09F100E /* libPods-defaults-ShareRocketChatRN.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -210,8 +210,9 @@
/* Begin PBXFileReference section */
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
016747EF3B9FED8DE2C9DA14 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-ShareRocketChatRN/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
04CABACAE3DF5FF44121FC30 /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = "<group>"; };
06BB44DD4855498082A744AD /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
09A9622AB95BAF321135704F /* libPods-defaults-Rocket.Chat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-Rocket.Chat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
10533AE1624775EF2CE700C5 /* libPods-defaults-NotificationService.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-NotificationService.a"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07F961A680F5B00A75B9A /* Rocket.Chat Experimental.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Rocket.Chat Experimental.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = RocketChatRN/AppDelegate.h; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = RocketChatRN/Images.xcassets; sourceTree = "<group>"; };
@ -264,14 +265,13 @@
1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
1EFEB5992493B6640072EDC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1EFEB5A12493B67D0072EDC0 /* NotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationService.entitlements; sourceTree = "<group>"; };
28F09CD56CC780BB681DA43A /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
391C4F7AA7023CD41EEBD106 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-Rocket.Chat/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
3982B430BE28C2D93FD9AF5C /* libPods-defaults-ShareRocketChatRN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-ShareRocketChatRN.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3AD43034372030994471D0E9 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = "<group>"; };
421FD6D28D6DECEEC09F100E /* libPods-defaults-ShareRocketChatRN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-ShareRocketChatRN.a"; sourceTree = BUILT_PRODUCTS_DIR; };
45D5C142B655F8EFD006792C /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-defaults-RocketChatRN/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
48A6FD916DB2F924F1360A4A /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = "<group>"; };
5F1EEB258E879574E6F9EADA /* libPods-defaults-Rocket.Chat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-Rocket.Chat.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5CA29716AE736DD49CEC421A /* Pods-defaults-Rocket.Chat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.debug.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.debug.xcconfig"; sourceTree = "<group>"; };
60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = "<group>"; };
674E4FB148AE2FB17415683F /* libPods-defaults-RocketChatRN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-RocketChatRN.a"; sourceTree = BUILT_PRODUCTS_DIR; };
77A439FBC388926A1D8F1B98 /* Pods-defaults-RocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.debug.xcconfig"; sourceTree = "<group>"; };
7A006F13229C83B600803143 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
7A0D62D1242AB187006D5C06 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
7A14FCEC257FEB3A005BDCD4 /* Experimental.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Experimental.xcassets; sourceTree = "<group>"; };
@ -282,14 +282,14 @@
7AAB3E52257E6A6E00707CF6 /* Rocket.Chat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Rocket.Chat.app; sourceTree = BUILT_PRODUCTS_DIR; };
7ACD4853222860DE00442C55 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
7AE10C0528A59530003593CB /* Inter.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Inter.ttf; sourceTree = "<group>"; };
7E01AFB7FFC99A24DE24A9E7 /* Pods-defaults-NotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.release.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.release.xcconfig"; sourceTree = "<group>"; };
8009E8CCFAA4804CCED401D8 /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
94EB1DBE281212E61157DDEE /* Pods-defaults-ShareRocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-ShareRocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN.release.xcconfig"; sourceTree = "<group>"; };
AC87BFDE8CC75468C2E87328 /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = "<group>"; };
8CF5ED249C29001A33922AC8 /* Pods-defaults-RocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-RocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN.release.xcconfig"; sourceTree = "<group>"; };
9EF17496557D766CD7CC65C8 /* Pods-defaults-NotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-NotificationService.debug.xcconfig"; path = "Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService.debug.xcconfig"; sourceTree = "<group>"; };
B37C79D9BD0742CE936B6982 /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
BA7E862283664608B3894E34 /* libWatermelonDB.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libWatermelonDB.a; sourceTree = "<group>"; };
C84BF59D4FEA8C08AD41906D /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-ShareRocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN.debug.xcconfig"; sourceTree = "<group>"; };
DEC0A8A825375FCCDCDFEFE2 /* libPods-defaults-NotificationService.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-NotificationService.a"; sourceTree = BUILT_PRODUCTS_DIR; };
D2549B70E3F3979B2FB2F170 /* Pods-defaults-Rocket.Chat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-Rocket.Chat.release.xcconfig"; path = "Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat.release.xcconfig"; sourceTree = "<group>"; };
E956695E229DFCCB2DB61EE2 /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-ShareRocketChatRN.debug.xcconfig"; path = "Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN.debug.xcconfig"; sourceTree = "<group>"; };
FA35FD80EDA9584432506DDA /* Pods-defaults-ShareRocketChatRN.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-defaults-ShareRocketChatRN.release.xcconfig"; path = "Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN.release.xcconfig"; sourceTree = "<group>"; };
FA66A470302CC036893877E3 /* libPods-defaults-RocketChatRN.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-defaults-RocketChatRN.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -310,7 +310,7 @@
7ACD4897222860DE00442C55 /* JavaScriptCore.framework in Frameworks */,
24A2AEF2383D44B586D31C01 /* libz.tbd in Frameworks */,
DD2BA30A89E64F189C2C24AC /* libWatermelonDB.a in Frameworks */,
58B1112437C7F012923203ED /* libPods-defaults-RocketChatRN.a in Frameworks */,
A891D4519F3C3C0DF281382E /* libPods-defaults-RocketChatRN.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -319,7 +319,7 @@
buildActionMask = 2147483647;
files = (
1E25743422CBA2CF005A877F /* JavaScriptCore.framework in Frameworks */,
4BC950FF98C56CFA992BBAFE /* libPods-defaults-ShareRocketChatRN.a in Frameworks */,
E5BA08EDF1C874FF35785B16 /* libPods-defaults-ShareRocketChatRN.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -327,7 +327,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A965681B9D9B1DB968676F54 /* libPods-defaults-NotificationService.a in Frameworks */,
77BB45FF3B406BBA2A3C36F9 /* libPods-defaults-NotificationService.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -348,7 +348,7 @@
7AAB3E3D257E6A6E00707CF6 /* JavaScriptCore.framework in Frameworks */,
7AAB3E3E257E6A6E00707CF6 /* libz.tbd in Frameworks */,
7AAB3E3F257E6A6E00707CF6 /* libWatermelonDB.a in Frameworks */,
0DAF353368B2DE2714B6DCE8 /* libPods-defaults-Rocket.Chat.a in Frameworks */,
D344DEDBA103E81285F48C9F /* libPods-defaults-Rocket.Chat.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -499,14 +499,14 @@
7AC2B09613AA7C3FEBAC9F57 /* Pods */ = {
isa = PBXGroup;
children = (
8009E8CCFAA4804CCED401D8 /* Pods-defaults-NotificationService.debug.xcconfig */,
7E01AFB7FFC99A24DE24A9E7 /* Pods-defaults-NotificationService.release.xcconfig */,
04CABACAE3DF5FF44121FC30 /* Pods-defaults-Rocket.Chat.debug.xcconfig */,
3AD43034372030994471D0E9 /* Pods-defaults-Rocket.Chat.release.xcconfig */,
48A6FD916DB2F924F1360A4A /* Pods-defaults-RocketChatRN.debug.xcconfig */,
AC87BFDE8CC75468C2E87328 /* Pods-defaults-RocketChatRN.release.xcconfig */,
C84BF59D4FEA8C08AD41906D /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */,
94EB1DBE281212E61157DDEE /* Pods-defaults-ShareRocketChatRN.release.xcconfig */,
9EF17496557D766CD7CC65C8 /* Pods-defaults-NotificationService.debug.xcconfig */,
28F09CD56CC780BB681DA43A /* Pods-defaults-NotificationService.release.xcconfig */,
5CA29716AE736DD49CEC421A /* Pods-defaults-Rocket.Chat.debug.xcconfig */,
D2549B70E3F3979B2FB2F170 /* Pods-defaults-Rocket.Chat.release.xcconfig */,
77A439FBC388926A1D8F1B98 /* Pods-defaults-RocketChatRN.debug.xcconfig */,
8CF5ED249C29001A33922AC8 /* Pods-defaults-RocketChatRN.release.xcconfig */,
E956695E229DFCCB2DB61EE2 /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */,
FA35FD80EDA9584432506DDA /* Pods-defaults-ShareRocketChatRN.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -597,10 +597,10 @@
7ACD4853222860DE00442C55 /* JavaScriptCore.framework */,
B37C79D9BD0742CE936B6982 /* libc++.tbd */,
06BB44DD4855498082A744AD /* libz.tbd */,
DEC0A8A825375FCCDCDFEFE2 /* libPods-defaults-NotificationService.a */,
5F1EEB258E879574E6F9EADA /* libPods-defaults-Rocket.Chat.a */,
674E4FB148AE2FB17415683F /* libPods-defaults-RocketChatRN.a */,
3982B430BE28C2D93FD9AF5C /* libPods-defaults-ShareRocketChatRN.a */,
10533AE1624775EF2CE700C5 /* libPods-defaults-NotificationService.a */,
09A9622AB95BAF321135704F /* libPods-defaults-Rocket.Chat.a */,
FA66A470302CC036893877E3 /* libPods-defaults-RocketChatRN.a */,
421FD6D28D6DECEEC09F100E /* libPods-defaults-ShareRocketChatRN.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -620,7 +620,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "RocketChatRN" */;
buildPhases = (
E368867D90711EA1666BFF6A /* [CP] Check Pods Manifest.lock */,
D9ECCB381CCB547C6D9734F3 /* [CP] Check Pods Manifest.lock */,
7AA5C63E23E30D110005C4A7 /* Start Packager */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
@ -629,8 +629,8 @@
1EC6ACF422CB9FC300A41C61 /* Embed App Extensions */,
1E1EA8082326CCE300E22452 /* ShellScript */,
7AAE9EB32891A0D20024F559 /* Upload source maps to Bugsnag */,
DE93C39DFDB4F3759B84670D /* [CP] Embed Pods Frameworks */,
52CE06391F137AA82A402A69 /* [CP] Copy Pods Resources */,
A4C45440ECC8DF4C333E90AC /* [CP] Embed Pods Frameworks */,
A820EB1B8BCA486D6F111B28 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -647,12 +647,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 1EC6ACF322CB9FC300A41C61 /* Build configuration list for PBXNativeTarget "ShareRocketChatRN" */;
buildPhases = (
943E61A9039C6FE02871CAD9 /* [CP] Check Pods Manifest.lock */,
1C5A0A2393598AD05D7E91C9 /* [CP] Check Pods Manifest.lock */,
1EC6ACAC22CB9FC300A41C61 /* Sources */,
1EC6ACAD22CB9FC300A41C61 /* Frameworks */,
1EC6ACAE22CB9FC300A41C61 /* Resources */,
1EFE4DC322CBF36300B766B7 /* ShellScript */,
1FB8D9B08356E6AC6201714D /* [CP] Copy Pods Resources */,
826C4D7E61DA0A8A695A5BF0 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -667,11 +667,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 1EFEB5A02493B6640072EDC0 /* Build configuration list for PBXNativeTarget "NotificationService" */;
buildPhases = (
AAFC671179263417C34C729A /* [CP] Check Pods Manifest.lock */,
443ECB36C026E4E5891D0289 /* [CP] Check Pods Manifest.lock */,
1EFEB5912493B6640072EDC0 /* Sources */,
1EFEB5922493B6640072EDC0 /* Frameworks */,
1EFEB5932493B6640072EDC0 /* Resources */,
996816F401E32166714ABD9E /* [CP] Copy Pods Resources */,
A02AEE64373B898BFED7C77A /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -686,7 +686,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 7AAB3E4F257E6A6E00707CF6 /* Build configuration list for PBXNativeTarget "Rocket.Chat" */;
buildPhases = (
CDFA7A6FA541A8BED7CE9EE7 /* [CP] Check Pods Manifest.lock */,
439D1EFA5403CEE68343332F /* [CP] Check Pods Manifest.lock */,
7AAB3E13257E6A6E00707CF6 /* Start Packager */,
7AAB3E14257E6A6E00707CF6 /* Sources */,
7AAB3E32257E6A6E00707CF6 /* Frameworks */,
@ -695,8 +695,8 @@
7AAB3E48257E6A6E00707CF6 /* Embed App Extensions */,
7AAB3E4B257E6A6E00707CF6 /* ShellScript */,
7A10288726B1D15200E47EF8 /* Upload source maps to Bugsnag */,
CCA2F3534731DC8EE1FAB416 /* [CP] Embed Pods Frameworks */,
DEE9CFB76371CE33773CB435 /* [CP] Copy Pods Resources */,
6B58CF06B9125764FD3EEB12 /* [CP] Embed Pods Frameworks */,
5E4E13E42C9F5F48755B0D71 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -846,6 +846,28 @@
shellPath = /bin/sh;
shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
1C5A0A2393598AD05D7E91C9 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-defaults-ShareRocketChatRN-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
1E1EA8082326CCE300E22452 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -880,69 +902,57 @@
shellPath = /bin/sh;
shellScript = "export EXTRA_PACKAGER_ARGS=\"--sourcemap-output $TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\"\nexport NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n";
};
1FB8D9B08356E6AC6201714D /* [CP] Copy Pods Resources */ = {
439D1EFA5403CEE68343332F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/iosMath/mathFonts.bundle",
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/mathFonts.bundle",
"$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN-resources.sh\"\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
52CE06391F137AA82A402A69 /* [CP] Copy Pods Resources */ = {
443ECB36C026E4E5891D0289 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-defaults-NotificationService-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
5E4E13E42C9F5F48755B0D71 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh",
"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
@ -989,7 +999,31 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh\"\n";
showEnvVarsInLog = 0;
};
6B58CF06B9125764FD3EEB12 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiMeetSDK/JitsiMeetSDK.framework/JitsiMeetSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiMeetSDK/WebRTC.framework/WebRTC",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JitsiMeetSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
7A10288726B1D15200E47EF8 /* Upload source maps to Bugsnag */ = {
@ -1095,29 +1129,63 @@
shellPath = /bin/sh;
shellScript = "SOURCE_MAP=\"$TMPDIR/$(md5 -qs \"$CONFIGURATION_BUILD_DIR\")-main.jsbundle.map\" ../node_modules/@bugsnag/react-native/bugsnag-react-native-xcode.sh\n";
};
943E61A9039C6FE02871CAD9 /* [CP] Check Pods Manifest.lock */ = {
826C4D7E61DA0A8A695A5BF0 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController/TOCropViewControllerBundle.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/iosMath/mathFonts.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-defaults-ShareRocketChatRN-checkManifestLockResult.txt",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/QBImagePicker.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/TOCropViewControllerBundle.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/mathFonts.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-ShareRocketChatRN/Pods-defaults-ShareRocketChatRN-resources.sh\"\n";
showEnvVarsInLog = 0;
};
996816F401E32166714ABD9E /* [CP] Copy Pods Resources */ = {
A02AEE64373B898BFED7C77A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1173,75 +1241,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-NotificationService/Pods-defaults-NotificationService-resources.sh\"\n";
showEnvVarsInLog = 0;
};
AAFC671179263417C34C729A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-defaults-NotificationService-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
CCA2F3534731DC8EE1FAB416 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiMeetSDK/JitsiMeetSDK.framework/JitsiMeetSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/JitsiMeetSDK/WebRTC.framework/WebRTC",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JitsiMeetSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
CDFA7A6FA541A8BED7CE9EE7 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-defaults-Rocket.Chat-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DE93C39DFDB4F3759B84670D /* [CP] Embed Pods Frameworks */ = {
A4C45440ECC8DF4C333E90AC /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1265,13 +1265,13 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
DEE9CFB76371CE33773CB435 /* [CP] Copy Pods Resources */ = {
A820EB1B8BCA486D6F111B28 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh",
"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/RNImageCropPicker/QBImagePicker.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
@ -1318,10 +1318,10 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-Rocket.Chat/Pods-defaults-Rocket.Chat-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-defaults-RocketChatRN/Pods-defaults-RocketChatRN-resources.sh\"\n";
showEnvVarsInLog = 0;
};
E368867D90711EA1666BFF6A /* [CP] Check Pods Manifest.lock */ = {
D9ECCB381CCB547C6D9734F3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -1500,7 +1500,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 48A6FD916DB2F924F1360A4A /* Pods-defaults-RocketChatRN.debug.xcconfig */;
baseConfigurationReference = 77A439FBC388926A1D8F1B98 /* Pods-defaults-RocketChatRN.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APPLICATION_EXTENSION_API_ONLY = NO;
@ -1557,7 +1557,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AC87BFDE8CC75468C2E87328 /* Pods-defaults-RocketChatRN.release.xcconfig */;
baseConfigurationReference = 8CF5ED249C29001A33922AC8 /* Pods-defaults-RocketChatRN.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APPLICATION_EXTENSION_API_ONLY = NO;
@ -1613,7 +1613,7 @@
};
1EC6ACBC22CB9FC300A41C61 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C84BF59D4FEA8C08AD41906D /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */;
baseConfigurationReference = E956695E229DFCCB2DB61EE2 /* Pods-defaults-ShareRocketChatRN.debug.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ANALYZER_NONNULL = YES;
@ -1681,7 +1681,7 @@
};
1EC6ACBD22CB9FC300A41C61 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 94EB1DBE281212E61157DDEE /* Pods-defaults-ShareRocketChatRN.release.xcconfig */;
baseConfigurationReference = FA35FD80EDA9584432506DDA /* Pods-defaults-ShareRocketChatRN.release.xcconfig */;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ANALYZER_NONNULL = YES;
@ -1748,7 +1748,7 @@
};
1EFEB59D2493B6640072EDC0 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8009E8CCFAA4804CCED401D8 /* Pods-defaults-NotificationService.debug.xcconfig */;
baseConfigurationReference = 9EF17496557D766CD7CC65C8 /* Pods-defaults-NotificationService.debug.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@ -1785,7 +1785,7 @@
};
1EFEB59E2493B6640072EDC0 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7E01AFB7FFC99A24DE24A9E7 /* Pods-defaults-NotificationService.release.xcconfig */;
baseConfigurationReference = 28F09CD56CC780BB681DA43A /* Pods-defaults-NotificationService.release.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@ -1822,7 +1822,7 @@
};
7AAB3E50257E6A6E00707CF6 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 04CABACAE3DF5FF44121FC30 /* Pods-defaults-Rocket.Chat.debug.xcconfig */;
baseConfigurationReference = 5CA29716AE736DD49CEC421A /* Pods-defaults-Rocket.Chat.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APPLICATION_EXTENSION_API_ONLY = NO;
@ -1876,7 +1876,7 @@
};
7AAB3E51257E6A6E00707CF6 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 3AD43034372030994471D0E9 /* Pods-defaults-Rocket.Chat.release.xcconfig */;
baseConfigurationReference = D2549B70E3F3979B2FB2F170 /* Pods-defaults-Rocket.Chat.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APPLICATION_EXTENSION_API_ONLY = NO;

View File

@ -18,7 +18,8 @@
"generate-source-maps-ios": "react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios-release.bundle --sourcemap-output ios-release.bundle.map",
"postinstall": "patch-package && jetify",
"prepare": "husky install",
"build-icon-set": "node scripts/build-icon-set.js"
"build-icon-set": "node scripts/build-icon-set.js",
"devtools": "react-devtools"
},
"lint-staged": {
"*.{js,ts,tsx}": [
@ -55,6 +56,7 @@
"@rocket.chat/message-parser": "^0.31.14",
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
"@rocket.chat/ui-kit": "^0.31.19",
"@shopify/flash-list": "^1.2.2",
"bytebuffer": "^5.0.1",
"color2k": "1.2.4",
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
@ -126,8 +128,8 @@
"react-native-ui-lib": "RocketChat/react-native-ui-lib",
"react-native-vector-icons": "9.1.0",
"react-native-webview": "10.3.2",
"react-redux": "^8.0.5",
"reactotron-react-native": "^5.0.3",
"react-redux": "8.0.5",
"reactotron-react-native": "5.0.3",
"redux": "4.2.0",
"redux-immutable-state-invariant": "2.1.0",
"redux-saga": "1.1.3",
@ -206,7 +208,10 @@
"otp.js": "1.2.0",
"patch-package": "6.4.7",
"prettier": "^2.3.2",
"react-devtools": "4.27.0",
"react-devtools-core": "4.27.0",
"react-dom": "17.0.1",
"react-native-flipper-performance-plugin": "^0.3.1",
"react-test-renderer": "17.0.2",
"reactotron-redux": "3.1.3",
"reactotron-redux-saga": "4.2.3",

View File

@ -0,0 +1,16 @@
diff --git a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
index c2146c5..5ce7900 100644
--- a/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
+++ b/node_modules/@shopify/flash-list/ios/Sources/AutoLayoutView.swift
@@ -231,7 +231,10 @@ import UIKit
/// Fixes footer position along with rest of the items
private func fixFooter() {
- guard !disableAutoLayout, let parentScrollView = getScrollView() else {
+ guard !disableAutoLayout,
+ // Fixing layout during animation can interfere with it.
+ layer.animationKeys()?.isEmpty ?? true,
+ let parentScrollView = getScrollView() else {
return
}

View File

@ -0,0 +1,230 @@
diff --git a/node_modules/react-native-ui-lib/lib/components/Keyboard/KeyboardInput/KeyboardAccessoryView.js b/node_modules/react-native-ui-lib/lib/components/Keyboard/KeyboardInput/KeyboardAccessoryView.js
index cfe1d35..7ce5105 100644
--- a/node_modules/react-native-ui-lib/lib/components/Keyboard/KeyboardInput/KeyboardAccessoryView.js
+++ b/node_modules/react-native-ui-lib/lib/components/Keyboard/KeyboardInput/KeyboardAccessoryView.js
@@ -123,7 +123,13 @@ class KeyboardAccessoryView extends Component {
* Whether or not to handle SafeArea
* default: true
*/
- useSafeArea: PropTypes.bool
+ useSafeArea: PropTypes.bool,
+
+ /**
+ * iOS only.
+ * Scroll view to track
+ */
+ scrollViewNativeID: PropTypes.string
};
static iosScrollBehaviors = IOS_SCROLL_BEHAVIORS;
@@ -256,7 +262,8 @@ class KeyboardAccessoryView extends Component {
kbComponent,
onItemSelected,
onRequestShowKeyboard,
- useSafeArea
+ useSafeArea,
+ scrollViewNativeID
} = this.props;
return (
@@ -271,6 +278,7 @@ class KeyboardAccessoryView extends Component {
addBottomView={addBottomView}
bottomViewColor={this.props.bottomViewColor}
allowHitsOutsideBounds={allowHitsOutsideBounds}
+ scrollViewNativeID={scrollViewNativeID}
>
{renderContent && renderContent()}
<CustomKeyboardView
diff --git a/node_modules/react-native-ui-lib/lib/ios/.DS_Store b/node_modules/react-native-ui-lib/lib/ios/.DS_Store
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomInputControllerTemp.h b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomInputControllerTemp.h
index b3864d0..e78322f 100644
--- a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomInputControllerTemp.h
+++ b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomInputControllerTemp.h
@@ -8,7 +8,7 @@
#if __has_include(<React/RCTEventEmitter.h>)
#import <React/RCTEventEmitter.h>
#else
-#import "RCTEventEmitter.h"
+#import <React/RCTEventEmitter.h>
#endif
@interface RCTCustomInputControllerTemp : RCTEventEmitter
diff --git a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewControllerTemp.h b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewControllerTemp.h
index 4344724..2786051 100644
--- a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewControllerTemp.h
+++ b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardinput/rctcustomInputcontroller/RCTCustomKeyboardViewControllerTemp.h
@@ -10,7 +10,7 @@
#if __has_include(<React/RCTRootView.h>)
#import <React/RCTRootView.h>
#else
-#import "RCTRootView.h"
+#import <React/RCTRootView.h>
#endif
@interface RCTCustomKeyboardViewControllerTemp : UIInputViewController
diff --git a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardtrackingview/KeyboardTrackingViewTempManager.m b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardtrackingview/KeyboardTrackingViewTempManager.m
index 8f8446e..728bedd 100644
--- a/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardtrackingview/KeyboardTrackingViewTempManager.m
+++ b/node_modules/react-native-ui-lib/lib/ios/reactnativeuilib/keyboardtrackingview/KeyboardTrackingViewTempManager.m
@@ -43,7 +43,6 @@ @interface KeyboardTrackingViewTemp : UIView
@property (nonatomic, strong) UIScrollView *scrollViewToManage;
@property (nonatomic) BOOL scrollIsInverted;
@property (nonatomic) BOOL revealKeyboardInteractive;
-@property (nonatomic) BOOL isDraggingScrollView;
@property (nonatomic) BOOL manageScrollView;
@property (nonatomic) BOOL requiresSameParentToManageScrollView;
@property (nonatomic) NSUInteger deferedInitializeAccessoryViewsCount;
@@ -54,6 +53,7 @@ @interface KeyboardTrackingViewTemp : UIView
@property (nonatomic) BOOL useSafeArea;
@property (nonatomic) BOOL scrollToFocusedInput;
@property (nonatomic) BOOL allowHitsOutsideBounds;
+@property (nonatomic) NSString* scrollViewNativeID;
@end
@@ -84,6 +84,7 @@ -(instancetype)init
self.addBottomView = NO;
self.bottomViewColor = nil;
self.scrollToFocusedInput = NO;
+ self.scrollIsInverted = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rctContentDidAppearNotification:) name:RCTContentDidAppearNotification object:nil];
}
@@ -170,7 +171,6 @@ -(void)layoutSubviews
- (void)initializeAccessoryViewsAndHandleInsets
{
NSArray<UIView*>* allSubviews = [self getBreadthFirstSubviewsForView:[self getRootView]];
- NSMutableArray<RCTScrollView*>* rctScrollViewsArray = [NSMutableArray array];
for (UIView* subview in allSubviews)
{
@@ -179,24 +179,13 @@ - (void)initializeAccessoryViewsAndHandleInsets
{
if(_scrollViewToManage == nil)
{
- if(_requiresSameParentToManageScrollView && [subview isKindOfClass:[RCTScrollView class]] && subview.superview == self.superview)
+ if([subview isKindOfClass:[RCTScrollView class]])
{
- _scrollViewToManage = ((RCTScrollView*)subview).scrollView;
+ RCTScrollView *scrollView = (RCTScrollView*)subview;
+ if (subview.nativeID && [subview.nativeID isEqualToString:self.scrollViewNativeID]) {
+ _scrollViewToManage = scrollView.scrollView;
+ }
}
- else if(!_requiresSameParentToManageScrollView && [subview isKindOfClass:[UIScrollView class]])
- {
- _scrollViewToManage = (UIScrollView*)subview;
- }
-
- if(_scrollViewToManage != nil)
- {
- _scrollIsInverted = CGAffineTransformEqualToTransform(_scrollViewToManage.superview.transform, CGAffineTransformMakeScale(1, -1));
- }
- }
-
- if([subview isKindOfClass:[RCTScrollView class]])
- {
- [rctScrollViewsArray addObject:(RCTScrollView*)subview];
}
}
@@ -246,15 +235,15 @@ - (void)initializeAccessoryViewsAndHandleInsets
}
}
- for (RCTScrollView *scrollView in rctScrollViewsArray)
- {
- if(scrollView.scrollView == _scrollViewToManage)
- {
- [scrollView removeScrollListener:self];
- [scrollView addScrollListener:self];
- break;
- }
- }
+// for (RCTScrollView *scrollView in [_rctScrollViewsArray allValues])
+// {
+// if(scrollView.scrollView == _scrollViewToManage)
+// {
+// [scrollView removeScrollListener:self];
+// [scrollView addScrollListener:self];
+// break;
+// }
+// }
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
if (@available(iOS 11.0, *)) {
@@ -412,9 +401,6 @@ - (void)_updateScrollViewInsets
CGFloat bottomSafeArea = [self getBottomSafeArea];
CGFloat bottomInset = MAX(self.bounds.size.height, _ObservingInputAccessoryViewTemp.keyboardHeight + _ObservingInputAccessoryViewTemp.height);
- CGFloat originalBottomInset = self.scrollIsInverted ? insets.top : insets.bottom;
- CGPoint originalOffset = self.scrollViewToManage.contentOffset;
-
bottomInset += (_ObservingInputAccessoryViewTemp.keyboardHeight == 0 ? bottomSafeArea : 0);
if(self.scrollIsInverted)
{
@@ -426,20 +412,11 @@ - (void)_updateScrollViewInsets
}
self.scrollViewToManage.contentInset = insets;
- if(self.scrollBehavior == KeyboardTrackingScrollBehaviorScrollToBottomInvertedOnly && _scrollIsInverted)
+ BOOL firstTime = _ObservingInputAccessoryViewTemp.keyboardHeight == 0 && _ObservingInputAccessoryViewTemp.keyboardState == KeyboardStateHidden;
+ BOOL willOpen = _ObservingInputAccessoryViewTemp.keyboardHeight != 0 && _ObservingInputAccessoryViewTemp.keyboardState == KeyboardStateHidden;
+ if(firstTime || willOpen)
{
- BOOL fisrtTime = _ObservingInputAccessoryViewTemp.keyboardHeight == 0 && _ObservingInputAccessoryViewTemp.keyboardState == KeyboardStateHidden;
- BOOL willOpen = _ObservingInputAccessoryViewTemp.keyboardHeight != 0 && _ObservingInputAccessoryViewTemp.keyboardState == KeyboardStateHidden;
- BOOL isOpen = _ObservingInputAccessoryViewTemp.keyboardHeight != 0 && _ObservingInputAccessoryViewTemp.keyboardState == KeyboardStateShown;
- if(fisrtTime || willOpen || (isOpen && !self.isDraggingScrollView))
- {
- [self.scrollViewToManage setContentOffset:CGPointMake(self.scrollViewToManage.contentOffset.x, -self.scrollViewToManage.contentInset.top) animated:!fisrtTime];
- }
- }
- else if(self.scrollBehavior == KeyboardTrackingScrollBehaviorFixedOffset && !self.isDraggingScrollView)
- {
- CGFloat insetsDiff = (bottomInset - originalBottomInset) * (self.scrollIsInverted ? -1 : 1);
- self.scrollViewToManage.contentOffset = CGPointMake(originalOffset.x, originalOffset.y + insetsDiff);
+ [self.scrollViewToManage setContentOffset:CGPointMake(self.scrollViewToManage.contentOffset.x, -self.scrollViewToManage.contentInset.top) animated:!firstTime];
}
insets = self.scrollViewToManage.contentInset;
@@ -468,7 +445,6 @@ -(void)addBottomViewIfNecessary
if (self.addBottomView && _bottomView == nil)
{
_bottomView = [UIView new];
- // _bottomView.backgroundColor = [UIColor whiteColor];
if (self.bottomViewColor)
{
_bottomView.backgroundColor = [self colorFromHexString:self.bottomViewColor];
@@ -607,21 +583,6 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView
}
}
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
-{
- self.isDraggingScrollView = YES;
-}
-
-- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
-{
- self.isDraggingScrollView = NO;
-}
-
-- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
-{
- self.isDraggingScrollView = NO;
-}
-
- (CGFloat)getKeyboardHeight
{
return _ObservingInputAccessoryViewTemp ? _ObservingInputAccessoryViewTemp.keyboardHeight : 0;
@@ -664,6 +625,7 @@ @implementation KeyboardTrackingViewTempManager
RCT_REMAP_VIEW_PROPERTY(useSafeArea, useSafeArea, BOOL)
RCT_REMAP_VIEW_PROPERTY(scrollToFocusedInput, scrollToFocusedInput, BOOL)
RCT_REMAP_VIEW_PROPERTY(allowHitsOutsideBounds, allowHitsOutsideBounds, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(scrollViewNativeID, NSString)
+ (BOOL)requiresMainQueueSetup
{

779
yarn.lock

File diff suppressed because it is too large Load Diff