Compare commits
59 Commits
develop
...
chore/ts-R
Author | SHA1 | Date |
---|---|---|
AlexAlexandre | 2bc39cc4b8 | |
Reinaldo Neto | 037c921f46 | |
AlexAlexandre | b26838f5ca | |
AlexAlexandre | 5e26954644 | |
AlexAlexandre | 6a9f878d28 | |
AlexAlexandre | 8192b391ea | |
AlexAlexandre | 11c347e222 | |
AlexAlexandre | 9d4f99ee84 | |
AlexAlexandre | cdcfbe5f35 | |
AlexAlexandre | 49e30dc723 | |
AlexAlexandre | 69c4e7a80b | |
AlexAlexandre | 4b9ed36fa5 | |
AlexAlexandre | f68a0ac145 | |
AlexAlexandre | 26d8d5b8dc | |
AlexAlexandre | 29f554caab | |
AlexAlexandre | 60583eb517 | |
AlexAlexandre | 18b2f3388d | |
AlexAlexandre | 09a8254813 | |
AlexAlexandre | 667f26e7cd | |
AlexAlexandre | d29b661ccc | |
AlexAlexandre | 63a3e91fd9 | |
AlexAlexandre | d649eb5f4e | |
AlexAlexandre | 5635fbbebb | |
AlexAlexandre | 13ee494d8a | |
AlexAlexandre | 91f73a5101 | |
AlexAlexandre | b211b5a948 | |
AlexAlexandre | 5622d1b25d | |
AlexAlexandre | 2ca5a06c5e | |
AlexAlexandre | 0694666a21 | |
AlexAlexandre | 0cf9405a65 | |
AlexAlexandre | 9519ae50e7 | |
AlexAlexandre | 475d6be5b0 | |
AlexAlexandre | e6a05f4434 | |
AlexAlexandre | 1dc978cc88 | |
AlexAlexandre | e738f5c80f | |
AlexAlexandre | 42d1744cc3 | |
AlexAlexandre | 228e073f58 | |
AlexAlexandre | 2d45e288fb | |
AlexAlexandre | bd34344a0c | |
AlexAlexandre | 2493cf3f59 | |
AlexAlexandre | ba2b9e862b | |
AlexAlexandre | 244c2a05a9 | |
AlexAlexandre | f68ed794d2 | |
AlexAlexandre | d675828c28 | |
AlexAlexandre | 8a4892e61e | |
AlexAlexandre | 55982566f7 | |
AlexAlexandre | 161633e299 | |
AlexAlexandre | 8239c4489a | |
AlexAlexandre | a6146bd02b | |
AlexAlexandre | dc3e21056e | |
AlexAlexandre | 6e5df501cb | |
AlexAlexandre | 46cf0f8e6f | |
AlexAlexandre | 9b746cf1e0 | |
AlexAlexandre | 87f7c15dfa | |
AlexAlexandre | b1d1e29d41 | |
AlexAlexandre | 7408cf88fe | |
AlexAlexandre | 1849c295a2 | |
AlexAlexandre | 3c5d0f127c | |
AlexAlexandre | 161e667678 |
|
@ -15,15 +15,11 @@ import { showConfirmationAlert } from '../../utils/info';
|
||||||
import { useActionSheet } from '../ActionSheet';
|
import { useActionSheet } from '../ActionSheet';
|
||||||
import Header, { HEADER_HEIGHT } from './Header';
|
import Header, { HEADER_HEIGHT } from './Header';
|
||||||
import events from '../../utils/log/events';
|
import events from '../../utils/log/events';
|
||||||
|
import { IRoom } from '../../definitions/IRoom';
|
||||||
|
|
||||||
interface IMessageActions {
|
interface IMessageActions {
|
||||||
room: {
|
room: IRoom;
|
||||||
rid: string | number;
|
tmid?: string;
|
||||||
autoTranslateLanguage: any;
|
|
||||||
autoTranslate: any;
|
|
||||||
reactWhenReadOnly: any;
|
|
||||||
};
|
|
||||||
tmid: string;
|
|
||||||
user: {
|
user: {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,11 +69,6 @@ interface IRCTextInputProps extends TextInputProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class RCTextInput extends React.PureComponent<IRCTextInputProps, any> {
|
export default class RCTextInput extends React.PureComponent<IRCTextInputProps, any> {
|
||||||
static defaultProps = {
|
|
||||||
error: {},
|
|
||||||
theme: 'light'
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
showPassword: false
|
showPassword: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IMention {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
type: string;
|
||||||
|
}
|
|
@ -1,3 +1,44 @@
|
||||||
export interface IMessage {
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
msg: string;
|
|
||||||
|
import { ISubscriptions } from './ISubscriptions';
|
||||||
|
|
||||||
|
export interface IMessage extends ISubscriptions {
|
||||||
|
id: string;
|
||||||
|
rid: string;
|
||||||
|
ts: number;
|
||||||
|
u: string;
|
||||||
|
alias: string;
|
||||||
|
parse_urls: string;
|
||||||
|
_updated_at: number;
|
||||||
|
msg?: string;
|
||||||
|
t?: string;
|
||||||
|
groupable?: boolean;
|
||||||
|
avatar?: string;
|
||||||
|
emoji?: string;
|
||||||
|
attachments?: string;
|
||||||
|
urls?: string;
|
||||||
|
status?: number;
|
||||||
|
pinned?: boolean;
|
||||||
|
starred?: boolean;
|
||||||
|
edited_by?: string;
|
||||||
|
reactions?: string;
|
||||||
|
role?: string;
|
||||||
|
drid?: string;
|
||||||
|
dcount?: number;
|
||||||
|
dlm?: number;
|
||||||
|
tmid?: string;
|
||||||
|
tcount?: number;
|
||||||
|
tlm?: number;
|
||||||
|
replies?: string;
|
||||||
|
mentions?: string;
|
||||||
|
channels?: string;
|
||||||
|
auto_translate?: boolean;
|
||||||
|
translations?: string;
|
||||||
|
tmsg?: string;
|
||||||
|
blocks?: string;
|
||||||
|
e2e?: string;
|
||||||
|
tshow?: boolean;
|
||||||
|
md?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TMessageModel = IMessage & Model;
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface IReaction {
|
||||||
|
_id: string;
|
||||||
|
emoji: string;
|
||||||
|
usernames: string[];
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
import { IRocketChatRecord } from './IRocketChatRecord';
|
import { IRocketChatRecord } from './IRocketChatRecord';
|
||||||
|
import { ISubscriptions } from './ISubscriptions';
|
||||||
|
|
||||||
export enum RoomType {
|
export enum RoomType {
|
||||||
GROUP = 'p',
|
GROUP = 'p',
|
||||||
DIRECT = 'd',
|
DIRECT = 'd',
|
||||||
CHANNEL = 'c',
|
CHANNEL = 'c',
|
||||||
OMNICHANNEL = 'l',
|
OMNICHANNEL = 'l',
|
||||||
THREAD = 'thread'
|
THREAD = 'thread',
|
||||||
|
E2E_MESSAGE_TYPE = 'e2e'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRoom extends IRocketChatRecord {
|
export interface IRoom extends IRocketChatRecord, ISubscriptions {
|
||||||
rid: string;
|
rid: string;
|
||||||
t: RoomType;
|
t: RoomType;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -20,8 +24,10 @@ export interface IRoom extends IRocketChatRecord {
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
encrypted?: boolean;
|
encrypted?: boolean;
|
||||||
visitor?: boolean;
|
visitor?: boolean;
|
||||||
autoTranslateLanguage?: boolean;
|
usedCannedResponse?: string;
|
||||||
autoTranslate?: boolean;
|
bannerClosed: boolean;
|
||||||
observe?: Function;
|
lastOpen?: Date;
|
||||||
usedCannedResponse: string;
|
draftMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TRoomModel = IRoom & Model;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
export interface ISubscriptions {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
fname: string;
|
||||||
|
rid: string;
|
||||||
|
unread: number;
|
||||||
|
tunread: string;
|
||||||
|
tunreadUser: string;
|
||||||
|
tunreadGroup: string;
|
||||||
|
joinCodeRequired: boolean;
|
||||||
|
alert: boolean;
|
||||||
|
userMentions: object;
|
||||||
|
ls: Date;
|
||||||
|
jitsiTimeout: number;
|
||||||
|
ignored: any;
|
||||||
|
announcement: string;
|
||||||
|
sysMes: string;
|
||||||
|
archived: string;
|
||||||
|
broadcast: string;
|
||||||
|
autoTranslateLanguage: string;
|
||||||
|
autoTranslate: boolean;
|
||||||
|
reactWhenReadOnly: boolean;
|
||||||
|
f: boolean;
|
||||||
|
ro: boolean;
|
||||||
|
blocked: boolean;
|
||||||
|
blocker: boolean;
|
||||||
|
muted: boolean;
|
||||||
|
roles: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSubscriptionsModel = ISubscriptions & Model;
|
|
@ -0,0 +1,45 @@
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
|
import { IAttachment } from './IAttachment';
|
||||||
|
import { IMention } from './IMention';
|
||||||
|
import { IReaction } from './IReaction';
|
||||||
|
import { RoomType } from './IRoom';
|
||||||
|
import { IUrl } from './IUrl';
|
||||||
|
|
||||||
|
export interface IThread {
|
||||||
|
id: string;
|
||||||
|
msg: string;
|
||||||
|
t: RoomType;
|
||||||
|
rid: string;
|
||||||
|
_updatedAt: Date;
|
||||||
|
ts: Date;
|
||||||
|
u: { _id: string; username: string; name: string };
|
||||||
|
alias: any;
|
||||||
|
parseUrls: any;
|
||||||
|
groupable: boolean;
|
||||||
|
avatar: string;
|
||||||
|
emoji: any;
|
||||||
|
attachments: IAttachment[];
|
||||||
|
urls: IUrl[];
|
||||||
|
status: number;
|
||||||
|
pinned: boolean;
|
||||||
|
starred: boolean;
|
||||||
|
editedBy: { username: string };
|
||||||
|
reactions: IReaction[];
|
||||||
|
role: string;
|
||||||
|
drid: string;
|
||||||
|
dcount: number;
|
||||||
|
dlm: number;
|
||||||
|
tmid: string;
|
||||||
|
tcount: number;
|
||||||
|
tlm: Date;
|
||||||
|
replies: string[];
|
||||||
|
mentions: IMention[];
|
||||||
|
channels: [];
|
||||||
|
unread: boolean;
|
||||||
|
autoTranslate: boolean;
|
||||||
|
translations: any;
|
||||||
|
e2e: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TThreadModel = IThread & Model;
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface IUrl {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
url: string;
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ export type ModalStackParamList = {
|
||||||
rid: string;
|
rid: string;
|
||||||
t: RoomType;
|
t: RoomType;
|
||||||
joined: boolean;
|
joined: boolean;
|
||||||
|
showCloseModal?: boolean;
|
||||||
};
|
};
|
||||||
RoomInfoView: {
|
RoomInfoView: {
|
||||||
room: IRoom;
|
room: IRoom;
|
||||||
|
@ -61,6 +62,9 @@ export type ModalStackParamList = {
|
||||||
t: RoomType;
|
t: RoomType;
|
||||||
encrypted?: boolean;
|
encrypted?: boolean;
|
||||||
showCloseModal?: boolean;
|
showCloseModal?: boolean;
|
||||||
|
room?: IRoom;
|
||||||
|
member?: any;
|
||||||
|
joined?: boolean;
|
||||||
};
|
};
|
||||||
SelectedUsersView: {
|
SelectedUsersView: {
|
||||||
maxUsers: number;
|
maxUsers: number;
|
||||||
|
|
|
@ -6,9 +6,12 @@ import { IOptionsField } from '../views/NotificationPreferencesView/options';
|
||||||
import { IServer } from '../definitions/IServer';
|
import { IServer } from '../definitions/IServer';
|
||||||
import { IAttachment } from '../definitions/IAttachment';
|
import { IAttachment } from '../definitions/IAttachment';
|
||||||
import { IMessage } from '../definitions/IMessage';
|
import { IMessage } from '../definitions/IMessage';
|
||||||
import { IRoom, RoomType } from '../definitions/IRoom';
|
import { IRoom, TRoomModel, RoomType } from '../definitions/IRoom';
|
||||||
|
import { ModalStackParamList } from './MasterDetailStack/types';
|
||||||
|
|
||||||
export type ChatsStackParamList = {
|
export type ChatsStackParamList = {
|
||||||
|
ModalStackNavigator: NavigatorScreenParams<ModalStackParamList>;
|
||||||
|
E2ESaveYourPasswordStackNavigator: NavigatorScreenParams<E2ESaveYourPasswordStackParamList>;
|
||||||
RoomsListView: undefined;
|
RoomsListView: undefined;
|
||||||
RoomView: {
|
RoomView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
|
@ -18,10 +21,11 @@ export type ChatsStackParamList = {
|
||||||
name?: string;
|
name?: string;
|
||||||
fname?: string;
|
fname?: string;
|
||||||
prid?: string;
|
prid?: string;
|
||||||
room: IRoom;
|
room?: TRoomModel;
|
||||||
jumpToMessageId?: string;
|
jumpToMessageId?: string;
|
||||||
jumpToThreadId?: string;
|
jumpToThreadId?: string;
|
||||||
roomUserId?: string;
|
roomUserId?: string;
|
||||||
|
usedCannedResponse?: boolean;
|
||||||
};
|
};
|
||||||
RoomActionsView: {
|
RoomActionsView: {
|
||||||
room: IRoom;
|
room: IRoom;
|
||||||
|
@ -45,6 +49,7 @@ export type ChatsStackParamList = {
|
||||||
member: any;
|
member: any;
|
||||||
rid: string;
|
rid: string;
|
||||||
t: RoomType;
|
t: RoomType;
|
||||||
|
showCloseModal?: boolean;
|
||||||
};
|
};
|
||||||
RoomInfoEditView: {
|
RoomInfoEditView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
|
@ -79,7 +84,7 @@ export type ChatsStackParamList = {
|
||||||
};
|
};
|
||||||
AutoTranslateView: {
|
AutoTranslateView: {
|
||||||
rid: string;
|
rid: string;
|
||||||
room: IRoom;
|
room: TRoomModel;
|
||||||
};
|
};
|
||||||
DirectoryView: undefined;
|
DirectoryView: undefined;
|
||||||
NotificationPrefView: {
|
NotificationPrefView: {
|
||||||
|
@ -143,6 +148,9 @@ export type ChatsStackParamList = {
|
||||||
};
|
};
|
||||||
room: IRoom;
|
room: IRoom;
|
||||||
};
|
};
|
||||||
|
AttachmentView: {
|
||||||
|
attachment: IAttachment;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProfileStackParamList = {
|
export type ProfileStackParamList = {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import getThreadName from '../../lib/methods/getThreadName';
|
import getThreadName from '../../lib/methods/getThreadName';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { IRoom, RoomType } from '../../definitions/IRoom';
|
import { IRoom, RoomType, TRoomModel } from '../../definitions/IRoom';
|
||||||
|
|
||||||
interface IMessagesViewProps {
|
interface IMessagesViewProps {
|
||||||
user: {
|
user: {
|
||||||
|
@ -78,7 +78,7 @@ interface IParams {
|
||||||
name?: string;
|
name?: string;
|
||||||
fname?: string;
|
fname?: string;
|
||||||
prid?: string;
|
prid?: string;
|
||||||
room: IRoom;
|
room: TRoomModel;
|
||||||
jumpToMessageId?: string;
|
jumpToMessageId?: string;
|
||||||
jumpToThreadId?: string;
|
jumpToThreadId?: string;
|
||||||
roomUserId?: string;
|
roomUserId?: string;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { BorderlessButton, ScrollView } from 'react-native-gesture-handler';
|
import { BorderlessButton, ScrollView } from 'react-native-gesture-handler';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
|
|
||||||
|
@ -9,8 +8,16 @@ import { CustomIcon } from '../../lib/Icons';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
interface IRoomBannerProps {
|
||||||
|
text: string;
|
||||||
|
title: string;
|
||||||
|
theme: string;
|
||||||
|
bannerClosed: boolean;
|
||||||
|
closeBanner(): void;
|
||||||
|
}
|
||||||
|
|
||||||
const Banner = React.memo(
|
const Banner = React.memo(
|
||||||
({ text, title, theme, bannerClosed, closeBanner }) => {
|
({ text, title, theme, bannerClosed, closeBanner }: IRoomBannerProps) => {
|
||||||
const [showModal, openModal] = useState(false);
|
const [showModal, openModal] = useState(false);
|
||||||
|
|
||||||
const toggleModal = () => openModal(prevState => !prevState);
|
const toggleModal = () => openModal(prevState => !prevState);
|
||||||
|
@ -22,6 +29,7 @@ const Banner = React.memo(
|
||||||
style={[styles.bannerContainer, { backgroundColor: themes[theme].bannerBackground }]}
|
style={[styles.bannerContainer, { backgroundColor: themes[theme].bannerBackground }]}
|
||||||
testID='room-view-banner'
|
testID='room-view-banner'
|
||||||
onPress={toggleModal}>
|
onPress={toggleModal}>
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<Markdown msg={text} theme={theme} numberOfLines={1} style={[styles.bannerText]} preview />
|
<Markdown msg={text} theme={theme} numberOfLines={1} style={[styles.bannerText]} preview />
|
||||||
<BorderlessButton onPress={closeBanner}>
|
<BorderlessButton onPress={closeBanner}>
|
||||||
<CustomIcon color={themes[theme].auxiliaryText} name='close' size={20} />
|
<CustomIcon color={themes[theme].auxiliaryText} name='close' size={20} />
|
||||||
|
@ -37,6 +45,7 @@ const Banner = React.memo(
|
||||||
<View style={[styles.modalView, { backgroundColor: themes[theme].bannerBackground }]}>
|
<View style={[styles.modalView, { backgroundColor: themes[theme].bannerBackground }]}>
|
||||||
<Text style={[styles.bannerModalTitle, { color: themes[theme].auxiliaryText }]}>{title}</Text>
|
<Text style={[styles.bannerModalTitle, { color: themes[theme].auxiliaryText }]}>{title}</Text>
|
||||||
<ScrollView style={styles.modalScrollView}>
|
<ScrollView style={styles.modalScrollView}>
|
||||||
|
{/* @ts-ignore*/}
|
||||||
<Markdown msg={text} theme={theme} />
|
<Markdown msg={text} theme={theme} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
|
@ -51,12 +60,4 @@ const Banner = React.memo(
|
||||||
prevProps.text === nextProps.text && prevProps.theme === nextProps.theme && prevProps.bannerClosed === nextProps.bannerClosed
|
prevProps.text === nextProps.text && prevProps.theme === nextProps.theme && prevProps.bannerClosed === nextProps.bannerClosed
|
||||||
);
|
);
|
||||||
|
|
||||||
Banner.propTypes = {
|
|
||||||
text: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
bannerClosed: PropTypes.bool,
|
|
||||||
closeBanner: PropTypes.func
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Banner;
|
export default Banner;
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ImageBackground, StyleSheet } from 'react-native';
|
import { ImageBackground, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
image: {
|
image: {
|
||||||
|
@ -10,17 +9,18 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const EmptyRoom = React.memo(({ length, mounted, theme, rid }) => {
|
interface IEmptyRoomProps {
|
||||||
|
length: number;
|
||||||
|
mounted: boolean;
|
||||||
|
theme: string;
|
||||||
|
rid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EmptyRoom = React.memo(({ length, mounted, theme, rid }: IEmptyRoomProps) => {
|
||||||
if ((length === 0 && mounted) || !rid) {
|
if ((length === 0 && mounted) || !rid) {
|
||||||
return <ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />;
|
return <ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
EmptyRoom.propTypes = {
|
|
||||||
length: PropTypes.number.isRequired,
|
|
||||||
mounted: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
rid: PropTypes.string
|
|
||||||
};
|
|
||||||
export default EmptyRoom;
|
export default EmptyRoom;
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { forwardRef, useImperativeHandle, useState } from 'react';
|
import React, { forwardRef, useImperativeHandle, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { InteractionManager, StyleSheet, Text, View } from 'react-native';
|
import { InteractionManager, StyleSheet, Text, View } from 'react-native';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -41,10 +40,18 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface IRoomJoinCodeProps {
|
||||||
|
rid: string;
|
||||||
|
t: string;
|
||||||
|
onJoin: Function;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
const JoinCode = React.memo(
|
const JoinCode = React.memo(
|
||||||
forwardRef(({ rid, t, onJoin, isMasterDetail, theme }, ref) => {
|
forwardRef(({ rid, t, onJoin, isMasterDetail, theme }: IRoomJoinCodeProps, ref) => {
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState<any>(false);
|
||||||
const [code, setCode] = useState('');
|
const [code, setCode] = useState('');
|
||||||
|
|
||||||
const show = () => setVisible(true);
|
const show = () => setVisible(true);
|
||||||
|
@ -64,7 +71,7 @@ const JoinCode = React.memo(
|
||||||
useImperativeHandle(ref, () => ({ show }));
|
useImperativeHandle(ref, () => ({ show }));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal transparent avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
|
<Modal avoidKeyboard useNativeDriver isVisible={visible} hideModalContentWhileAnimating>
|
||||||
<View style={styles.container} testID='join-code'>
|
<View style={styles.container} testID='join-code'>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
@ -76,7 +83,7 @@ const JoinCode = React.memo(
|
||||||
<TextInput
|
<TextInput
|
||||||
value={code}
|
value={code}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
inputRef={e => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
|
inputRef={(e: any) => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
|
||||||
returnKeyType='send'
|
returnKeyType='send'
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
onChangeText={setCode}
|
onChangeText={setCode}
|
||||||
|
@ -111,15 +118,8 @@ const JoinCode = React.memo(
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
JoinCode.propTypes = {
|
|
||||||
rid: PropTypes.string,
|
|
||||||
t: PropTypes.string,
|
|
||||||
onJoin: PropTypes.func,
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, null, null, { forwardRef: true })(JoinCode);
|
export default connect(mapStateToProps, null, null, { forwardRef: true })(JoinCode);
|
|
@ -1,10 +1,10 @@
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import { HeaderBackButton } from '@react-navigation/stack';
|
import { HeaderBackButton, StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
avatar: {
|
avatar: {
|
||||||
|
@ -13,10 +13,36 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface IRoomLeftButtonsProps {
|
||||||
|
tmid?: string;
|
||||||
|
unreadsCount: number & string;
|
||||||
|
navigation: StackNavigationProp<ChatsStackParamList>;
|
||||||
|
baseUrl: string;
|
||||||
|
userId: string;
|
||||||
|
token: string;
|
||||||
|
title: string;
|
||||||
|
t: string;
|
||||||
|
theme: string;
|
||||||
|
goRoomActionsView: Function;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const LeftButtons = React.memo(
|
const LeftButtons = React.memo(
|
||||||
({ tmid, unreadsCount, navigation, baseUrl, userId, token, title, t, theme, goRoomActionsView, isMasterDetail }) => {
|
({
|
||||||
|
tmid,
|
||||||
|
unreadsCount,
|
||||||
|
navigation,
|
||||||
|
baseUrl,
|
||||||
|
userId,
|
||||||
|
token,
|
||||||
|
title,
|
||||||
|
t,
|
||||||
|
theme,
|
||||||
|
goRoomActionsView,
|
||||||
|
isMasterDetail
|
||||||
|
}: IRoomLeftButtonsProps) => {
|
||||||
if (!isMasterDetail || tmid) {
|
if (!isMasterDetail || tmid) {
|
||||||
const onPress = useCallback(() => navigation.goBack());
|
const onPress = useCallback(() => navigation.goBack(), []);
|
||||||
const label = unreadsCount > 99 ? '+99' : unreadsCount || ' ';
|
const label = unreadsCount > 99 ? '+99' : unreadsCount || ' ';
|
||||||
const labelLength = label.length ? label.length : 1;
|
const labelLength = label.length ? label.length : 1;
|
||||||
const marginLeft = -2 * labelLength;
|
const marginLeft = -2 * labelLength;
|
||||||
|
@ -39,18 +65,4 @@ const LeftButtons = React.memo(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
LeftButtons.propTypes = {
|
|
||||||
tmid: PropTypes.string,
|
|
||||||
unreadsCount: PropTypes.number,
|
|
||||||
navigation: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
userId: PropTypes.string,
|
|
||||||
token: PropTypes.string,
|
|
||||||
title: PropTypes.string,
|
|
||||||
t: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
goRoomActionsView: PropTypes.func,
|
|
||||||
isMasterDetail: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LeftButtons;
|
export default LeftButtons;
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FlatList, StyleSheet } from 'react-native';
|
import { FlatList, FlatListProps, StyleSheet } from 'react-native';
|
||||||
import Animated from 'react-native-reanimated';
|
import Animated from 'react-native-reanimated';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { isIOS } from '../../../utils/deviceInfo';
|
import { isIOS } from '../../../utils/deviceInfo';
|
||||||
import scrollPersistTaps from '../../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../../utils/scrollPersistTaps';
|
||||||
|
import { IRoomItem } from '../index';
|
||||||
|
import { IMessage } from '../../../definitions/IMessage';
|
||||||
|
|
||||||
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
|
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
|
||||||
|
|
||||||
|
@ -17,11 +18,16 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const List = ({ listRef, ...props }) => (
|
interface IRoomListProps extends FlatListProps<IRoomItem | IMessage> {
|
||||||
|
listRef: React.Ref<FlatList>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const List = ({ listRef, ...props }: IRoomListProps): JSX.Element => (
|
||||||
<AnimatedFlatList
|
<AnimatedFlatList
|
||||||
testID='room-view-messages'
|
testID='room-view-messages'
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
keyExtractor={item => item.id}
|
// @ts-ignore
|
||||||
|
keyExtractor={(item: IRoomItem) => item.id}
|
||||||
contentContainerStyle={styles.contentContainer}
|
contentContainerStyle={styles.contentContainer}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
inverted
|
inverted
|
||||||
|
@ -35,8 +41,4 @@ const List = ({ listRef, ...props }) => (
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
List.propTypes = {
|
|
||||||
listRef: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
export default List;
|
export default List;
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import Animated, { call, cond, greaterOrEq, useCode, Value } from 'react-native-reanimated';
|
||||||
import Animated, { call, cond, greaterOrEq, useCode } from 'react-native-reanimated';
|
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { CustomIcon } from '../../../lib/Icons';
|
import { CustomIcon } from '../../../lib/Icons';
|
||||||
|
@ -30,11 +29,17 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const NavBottomFAB = ({ y, onPress, isThread }) => {
|
interface IRoomNavBottomFAB {
|
||||||
|
y: Value<number>;
|
||||||
|
onPress: Function;
|
||||||
|
isThread: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NavBottomFAB = ({ y, onPress, isThread }: IRoomNavBottomFAB) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
const handleOnPress = useCallback(() => onPress());
|
const handleOnPress = useCallback(() => onPress(), []);
|
||||||
const toggle = v => setShow(v);
|
const toggle = (v: boolean) => setShow(v);
|
||||||
|
|
||||||
useCode(
|
useCode(
|
||||||
() =>
|
() =>
|
||||||
|
@ -65,10 +70,4 @@ const NavBottomFAB = ({ y, onPress, isThread }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
NavBottomFAB.propTypes = {
|
|
||||||
y: Animated.Value,
|
|
||||||
onPress: PropTypes.func,
|
|
||||||
isThread: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NavBottomFAB;
|
export default NavBottomFAB;
|
|
@ -1,10 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RefreshControl } from 'react-native';
|
import { FlatList, NativeScrollEvent, NativeSyntheticEvent, RefreshControl, ViewToken } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { Value, event } from 'react-native-reanimated';
|
import { Value, event } from 'react-native-reanimated';
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import database from '../../../lib/database';
|
import database from '../../../lib/database';
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
@ -17,10 +18,14 @@ import debounce from '../../../utils/debounce';
|
||||||
import { compareServerVersion, methods } from '../../../lib/utils';
|
import { compareServerVersion, methods } from '../../../lib/utils';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
import NavBottomFAB from './NavBottomFAB';
|
import NavBottomFAB from './NavBottomFAB';
|
||||||
|
import { ChatsStackParamList } from '../../../stacks/types';
|
||||||
|
import { IRoomItem } from '../index';
|
||||||
|
import { IThread } from '../../../definitions/IThread';
|
||||||
|
import { IMessage, TMessageModel } from '../../../definitions/IMessage';
|
||||||
|
|
||||||
const QUERY_SIZE = 50;
|
const QUERY_SIZE = 50;
|
||||||
|
|
||||||
const onScroll = ({ y }) =>
|
const onScroll = ({ y }: { y: Value<number> }) =>
|
||||||
event(
|
event(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -32,23 +37,43 @@ const onScroll = ({ y }) =>
|
||||||
{ useNativeDriver: true }
|
{ useNativeDriver: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
class ListContainer extends React.Component {
|
interface IRoomListContainerProps {
|
||||||
static propTypes = {
|
renderRow: Function;
|
||||||
renderRow: PropTypes.func,
|
rid: string;
|
||||||
rid: PropTypes.string,
|
tmid?: string;
|
||||||
tmid: PropTypes.string,
|
theme: string;
|
||||||
theme: PropTypes.string,
|
loading: boolean;
|
||||||
loading: PropTypes.bool,
|
listRef: React.RefObject<FlatList>;
|
||||||
listRef: PropTypes.func,
|
hideSystemMessages: any[];
|
||||||
hideSystemMessages: PropTypes.array,
|
tunread: string;
|
||||||
tunread: PropTypes.array,
|
ignored: [];
|
||||||
ignored: PropTypes.array,
|
navigation: StackNavigationProp<ChatsStackParamList>;
|
||||||
navigation: PropTypes.object,
|
showMessageInMainThread: boolean;
|
||||||
showMessageInMainThread: PropTypes.bool,
|
serverVersion: string;
|
||||||
serverVersion: PropTypes.string
|
}
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
interface IRoomListContainerState {
|
||||||
|
messages: TMessageModel[];
|
||||||
|
refreshing: boolean;
|
||||||
|
highlightedMessage: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListContainer extends React.Component<IRoomListContainerProps, IRoomListContainerState> {
|
||||||
|
private count: number;
|
||||||
|
private mounted: boolean;
|
||||||
|
private animated: boolean;
|
||||||
|
private jumping: boolean;
|
||||||
|
private y: Value<number>;
|
||||||
|
private onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
|
||||||
|
private unsubscribeFocus: () => void;
|
||||||
|
private viewabilityConfig: { itemVisiblePercentThreshold: number };
|
||||||
|
private highlightedMessageTimeout?: ReturnType<typeof setTimeout> | false;
|
||||||
|
private thread?: IThread;
|
||||||
|
private messagesObservable?: Observable<TMessageModel[]>;
|
||||||
|
private messagesSubscription?: Subscription;
|
||||||
|
private viewableItems?: ViewToken[];
|
||||||
|
|
||||||
|
constructor(props: IRoomListContainerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
console.time(`${this.constructor.name} init`);
|
console.time(`${this.constructor.name} init`);
|
||||||
console.time(`${this.constructor.name} mount`);
|
console.time(`${this.constructor.name} mount`);
|
||||||
|
@ -78,7 +103,7 @@ class ListContainer extends React.Component {
|
||||||
console.timeEnd(`${this.constructor.name} mount`);
|
console.timeEnd(`${this.constructor.name} mount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: IRoomListContainerProps, nextState: IRoomListContainerState) {
|
||||||
const { refreshing, highlightedMessage } = this.state;
|
const { refreshing, highlightedMessage } = this.state;
|
||||||
const { hideSystemMessages, theme, tunread, ignored, loading } = this.props;
|
const { hideSystemMessages, theme, tunread, ignored, loading } = this.props;
|
||||||
if (theme !== nextProps.theme) {
|
if (theme !== nextProps.theme) {
|
||||||
|
@ -105,7 +130,7 @@ class ListContainer extends React.Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: IRoomListContainerProps) {
|
||||||
const { hideSystemMessages } = this.props;
|
const { hideSystemMessages } = this.props;
|
||||||
if (!dequal(hideSystemMessages, prevProps.hideSystemMessages)) {
|
if (!dequal(hideSystemMessages, prevProps.hideSystemMessages)) {
|
||||||
this.reload();
|
this.reload();
|
||||||
|
@ -114,9 +139,6 @@ class ListContainer extends React.Component {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unsubscribeMessages();
|
this.unsubscribeMessages();
|
||||||
if (this.onEndReached && this.onEndReached.stop) {
|
|
||||||
this.onEndReached.stop();
|
|
||||||
}
|
|
||||||
if (this.unsubscribeFocus) {
|
if (this.unsubscribeFocus) {
|
||||||
this.unsubscribeFocus();
|
this.unsubscribeFocus();
|
||||||
}
|
}
|
||||||
|
@ -160,6 +182,7 @@ class ListContainer extends React.Component {
|
||||||
Q.experimentalTake(this.count)
|
Q.experimentalTake(this.count)
|
||||||
];
|
];
|
||||||
if (!showMessageInMainThread) {
|
if (!showMessageInMainThread) {
|
||||||
|
// @ts-ignore
|
||||||
whereClause.push(Q.or(Q.where('tmid', null), Q.where('tshow', Q.eq(true))));
|
whereClause.push(Q.or(Q.where('tmid', null), Q.where('tshow', Q.eq(true))));
|
||||||
}
|
}
|
||||||
this.messagesObservable = db.collections
|
this.messagesObservable = db.collections
|
||||||
|
@ -170,8 +193,9 @@ class ListContainer extends React.Component {
|
||||||
|
|
||||||
if (rid) {
|
if (rid) {
|
||||||
this.unsubscribeMessages();
|
this.unsubscribeMessages();
|
||||||
this.messagesSubscription = this.messagesObservable.subscribe(messages => {
|
this.messagesSubscription = this.messagesObservable?.subscribe((messages: TMessageModel[]) => {
|
||||||
if (tmid && this.thread) {
|
if (tmid && this.thread) {
|
||||||
|
// @ts-ignore
|
||||||
messages = [...messages, this.thread];
|
messages = [...messages, this.thread];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,12 +204,13 @@ class ListContainer extends React.Component {
|
||||||
* hide system message is enabled
|
* hide system message is enabled
|
||||||
*/
|
*/
|
||||||
if (compareServerVersion(serverVersion, '3.16.0', methods.lowerThan) || hideSystemMessages.length) {
|
if (compareServerVersion(serverVersion, '3.16.0', methods.lowerThan) || hideSystemMessages.length) {
|
||||||
messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
|
messages = messages.filter((m: TMessageModel) => !m.t || !hideSystemMessages?.includes(m.t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ messages }, () => this.update());
|
this.setState({ messages }, () => this.update());
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
this.state.messages = messages;
|
this.state.messages = messages;
|
||||||
}
|
}
|
||||||
// TODO: move it away from here
|
// TODO: move it away from here
|
||||||
|
@ -254,21 +279,21 @@ class ListContainer extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
handleScrollToIndexFailed = params => {
|
handleScrollToIndexFailed = (params: { highestMeasuredFrameIndex: number }) => {
|
||||||
const { listRef } = this.props;
|
const { listRef } = this.props;
|
||||||
listRef.current.getNode().scrollToIndex({ index: params.highestMeasuredFrameIndex, animated: false });
|
listRef.current?.scrollToIndex({ index: params.highestMeasuredFrameIndex, animated: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
jumpToMessage = messageId =>
|
jumpToMessage = (messageId: string): Promise<void> =>
|
||||||
new Promise(async resolve => {
|
new Promise(async resolve => {
|
||||||
this.jumping = true;
|
this.jumping = true;
|
||||||
const { messages } = this.state;
|
const { messages } = this.state;
|
||||||
const { listRef } = this.props;
|
const { listRef } = this.props;
|
||||||
const index = messages.findIndex(item => item.id === messageId);
|
const index = messages.findIndex((item: TMessageModel) => item.id === messageId);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
listRef.current.getNode().scrollToIndex({ index, viewPosition: 0.5, viewOffset: 100 });
|
listRef.current?.scrollToIndex({ index, viewPosition: 0.5, viewOffset: 100 });
|
||||||
await new Promise(res => setTimeout(res, 300));
|
await new Promise(res => setTimeout(res, 300));
|
||||||
if (!this.viewableItems.map(vi => vi.key).includes(messageId)) {
|
if (!this.viewableItems?.map((vi: { key: string }) => vi.key).includes(messageId)) {
|
||||||
if (!this.jumping) {
|
if (!this.jumping) {
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
|
@ -282,7 +307,7 @@ class ListContainer extends React.Component {
|
||||||
}, 10000);
|
}, 10000);
|
||||||
await setTimeout(() => resolve(), 300);
|
await setTimeout(() => resolve(), 300);
|
||||||
} else {
|
} else {
|
||||||
listRef.current.getNode().scrollToIndex({ index: messages.length - 1, animated: false });
|
listRef.current?.scrollToIndex({ index: messages.length - 1, animated: false });
|
||||||
if (!this.jumping) {
|
if (!this.jumping) {
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
|
@ -297,7 +322,7 @@ class ListContainer extends React.Component {
|
||||||
|
|
||||||
jumpToBottom = () => {
|
jumpToBottom = () => {
|
||||||
const { listRef } = this.props;
|
const { listRef } = this.props;
|
||||||
listRef.current.getNode().scrollToOffset({ offset: -100 });
|
listRef.current?.scrollToOffset({ offset: -100 });
|
||||||
};
|
};
|
||||||
|
|
||||||
renderFooter = () => {
|
renderFooter = () => {
|
||||||
|
@ -308,13 +333,13 @@ class ListContainer extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderItem = ({ item, index }) => {
|
renderItem = ({ item, index }: { item: IRoomItem | IMessage; index: number }) => {
|
||||||
const { messages, highlightedMessage } = this.state;
|
const { messages, highlightedMessage } = this.state;
|
||||||
const { renderRow } = this.props;
|
const { renderRow } = this.props;
|
||||||
return renderRow(item, messages[index + 1], highlightedMessage);
|
return renderRow(item, messages[index + 1], highlightedMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
onViewableItemsChanged = ({ viewableItems }) => {
|
onViewableItemsChanged = ({ viewableItems }: { viewableItems: ViewToken[] }) => {
|
||||||
this.viewableItems = viewableItems;
|
this.viewableItems = viewableItems;
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,7 @@ stories.add('basic', () => (
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
|
|
||||||
const ThemeStory = ({ theme }) => (
|
const ThemeStory = ({ theme }: { theme: string }) => (
|
||||||
<ThemeContext.Provider value={{ theme }}>
|
<ThemeContext.Provider value={{ theme }}>
|
||||||
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||||
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK} />
|
<LoadMore load={load} type={MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK} />
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { ActivityIndicator, StyleSheet, Text } from 'react-native';
|
import { ActivityIndicator, StyleSheet, Text } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../../constants/messageTypeLoad';
|
import { MESSAGE_TYPE_LOAD_NEXT_CHUNK, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK } from '../../../constants/messageTypeLoad';
|
||||||
|
@ -21,7 +20,13 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const LoadMore = ({ load, type, runOnRender }) => {
|
interface IRoomLoadMoreProps {
|
||||||
|
load(): any;
|
||||||
|
type?: string;
|
||||||
|
runOnRender?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoadMore = ({ load, type, runOnRender }: IRoomLoadMoreProps) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
@ -62,10 +67,4 @@ const LoadMore = ({ load, type, runOnRender }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadMore.propTypes = {
|
|
||||||
load: PropTypes.func,
|
|
||||||
type: PropTypes.string,
|
|
||||||
runOnRender: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LoadMore;
|
export default LoadMore;
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
|
@ -9,29 +8,30 @@ import { isAndroid } from '../../utils/deviceInfo';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
|
||||||
|
interface IRoomReactionPickerProps {
|
||||||
|
baseUrl: string;
|
||||||
|
message: IMessage;
|
||||||
|
show: boolean;
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
reactionClose(): void;
|
||||||
|
onEmojiSelected(shortname: string, messageId: string): void;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
const margin = isAndroid ? 40 : 20;
|
const margin = isAndroid ? 40 : 20;
|
||||||
const maxSize = 400;
|
const maxSize = 400;
|
||||||
|
|
||||||
class ReactionPicker extends React.Component {
|
class ReactionPicker extends React.Component<IRoomReactionPickerProps, any> {
|
||||||
static propTypes = {
|
shouldComponentUpdate(nextProps: IRoomReactionPickerProps) {
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
message: PropTypes.object,
|
|
||||||
show: PropTypes.bool,
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
reactionClose: PropTypes.func,
|
|
||||||
onEmojiSelected: PropTypes.func,
|
|
||||||
width: PropTypes.number,
|
|
||||||
height: PropTypes.number,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
|
||||||
const { show, width, height } = this.props;
|
const { show, width, height } = this.props;
|
||||||
return nextProps.show !== show || width !== nextProps.width || height !== nextProps.height;
|
return nextProps.show !== show || width !== nextProps.width || height !== nextProps.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEmojiSelected = (emoji, shortname) => {
|
onEmojiSelected = (emoji: string, shortname: string) => {
|
||||||
// standard emojis: `emoji` is unicode and `shortname` is :joy:
|
// standard emojis: `emoji` is unicode and `shortname` is :joy:
|
||||||
// custom emojis: only `emoji` is returned with shortname type (:joy:)
|
// custom emojis: only `emoji` is returned with shortname type (:joy:)
|
||||||
// to set reactions, we need shortname type
|
// to set reactions, we need shortname type
|
||||||
|
@ -68,20 +68,17 @@ class ReactionPicker extends React.Component {
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
testID='reaction-picker'>
|
testID='reaction-picker'>
|
||||||
<EmojiPicker
|
<EmojiPicker onEmojiSelected={this.onEmojiSelected} baseUrl={baseUrl} />
|
||||||
// tabEmojiStyle={tabEmojiStyle}
|
|
||||||
onEmojiSelected={this.onEmojiSelected}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
baseUrl: state.server.server,
|
baseUrl: state.server.server,
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(withTheme(ReactionPicker));
|
// TODO remove this any after merge the HOCs PR
|
||||||
|
export default connect(mapStateToProps)(withTheme(ReactionPicker)) as any;
|
|
@ -1,30 +1,38 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import { getUserSelector } from '../../selectors/login';
|
import { getUserSelector } from '../../selectors/login';
|
||||||
import { events, logEvent } from '../../utils/log';
|
import { events, logEvent } from '../../utils/log';
|
||||||
import { isTeamRoom } from '../../utils/room';
|
import { isTeamRoom } from '../../utils/room';
|
||||||
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
|
import { RoomType } from '../../definitions/IRoom';
|
||||||
|
import { IThread, TThreadModel } from '../../definitions/IThread';
|
||||||
|
import { ISubscriptions, TSubscriptionsModel } from '../../definitions/ISubscriptions';
|
||||||
|
|
||||||
class RightButtonsContainer extends Component {
|
interface IRoomRightButtonsContainerProps {
|
||||||
static propTypes = {
|
userId: string;
|
||||||
userId: PropTypes.string,
|
threadsEnabled: boolean;
|
||||||
threadsEnabled: PropTypes.bool,
|
rid: string;
|
||||||
rid: PropTypes.string,
|
t: RoomType;
|
||||||
t: PropTypes.string,
|
tmid?: string;
|
||||||
tmid: PropTypes.string,
|
teamId: string;
|
||||||
teamId: PropTypes.string,
|
navigation: StackNavigationProp<ChatsStackParamList>;
|
||||||
navigation: PropTypes.object,
|
isMasterDetail: boolean;
|
||||||
isMasterDetail: PropTypes.bool,
|
toggleFollowThread: Function;
|
||||||
toggleFollowThread: PropTypes.func,
|
joined: boolean;
|
||||||
joined: PropTypes.bool,
|
encrypted: boolean;
|
||||||
encrypted: PropTypes.bool
|
}
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
class RightButtonsContainer extends Component<IRoomRightButtonsContainerProps, any> {
|
||||||
|
private threadSubscription?: Subscription;
|
||||||
|
private subSubscription?: Subscription;
|
||||||
|
|
||||||
|
constructor(props: IRoomRightButtonsContainerProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
isFollowingThread: true,
|
isFollowingThread: true,
|
||||||
|
@ -56,7 +64,7 @@ class RightButtonsContainer extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: IRoomRightButtonsContainerProps, nextState: any) {
|
||||||
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
const { isFollowingThread, tunread, tunreadUser, tunreadGroup } = this.state;
|
||||||
const { teamId } = this.props;
|
const { teamId } = this.props;
|
||||||
if (nextProps.teamId !== teamId) {
|
if (nextProps.teamId !== teamId) {
|
||||||
|
@ -86,26 +94,26 @@ class RightButtonsContainer extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
observeThread = threadRecord => {
|
observeThread = (threadRecord: TThreadModel) => {
|
||||||
const threadObservable = threadRecord.observe();
|
const threadObservable = threadRecord.observe();
|
||||||
this.threadSubscription = threadObservable.subscribe(thread => this.updateThread(thread));
|
this.threadSubscription = threadObservable.subscribe(thread => this.updateThread(thread));
|
||||||
};
|
};
|
||||||
|
|
||||||
updateThread = thread => {
|
updateThread = (thread: IThread) => {
|
||||||
const { userId } = this.props;
|
const { userId } = this.props;
|
||||||
this.setState({
|
this.setState({
|
||||||
isFollowingThread: thread.replies && !!thread.replies.find(t => t === userId)
|
isFollowingThread: thread.replies && !!thread.replies.find((t: string) => t === userId)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
observeSubscription = subRecord => {
|
observeSubscription = (subRecord: TSubscriptionsModel) => {
|
||||||
const subObservable = subRecord.observe();
|
const subObservable = subRecord.observe();
|
||||||
this.subSubscription = subObservable.subscribe(sub => {
|
this.subSubscription = subObservable.subscribe(sub => {
|
||||||
this.updateSubscription(sub);
|
this.updateSubscription(sub);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSubscription = sub => {
|
updateSubscription = (sub: ISubscriptions) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
tunread: sub?.tunread,
|
tunread: sub?.tunread,
|
||||||
tunreadUser: sub?.tunreadUser,
|
tunreadUser: sub?.tunreadUser,
|
||||||
|
@ -142,7 +150,7 @@ class RightButtonsContainer extends Component {
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
navigation.navigate('ModalStackNavigator', {
|
navigation.navigate('ModalStackNavigator', {
|
||||||
screen: 'SearchMessagesView',
|
screen: 'SearchMessagesView',
|
||||||
params: { rid, showCloseModal: true, encrypted }
|
params: { rid, t, showCloseModal: true, encrypted }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
navigation.navigate('SearchMessagesView', { rid, t, encrypted });
|
navigation.navigate('SearchMessagesView', { rid, t, encrypted });
|
||||||
|
@ -194,7 +202,7 @@ class RightButtonsContainer extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
userId: getUserSelector(state).id,
|
userId: getUserSelector(state).id,
|
||||||
threadsEnabled: state.settings.Threads_enabled,
|
threadsEnabled: state.settings.Threads_enabled,
|
||||||
isMasterDetail: state.app.isMasterDetail
|
isMasterDetail: state.app.isMasterDetail
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
@ -34,7 +33,13 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const DateSeparator = React.memo(({ ts, unread, theme }) => {
|
interface IRoomDateSeparatorProps {
|
||||||
|
ts?: Date | null;
|
||||||
|
unread: boolean;
|
||||||
|
theme: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DateSeparator = React.memo(({ ts, unread, theme }: IRoomDateSeparatorProps) => {
|
||||||
const date = ts ? moment(ts).format('LL') : null;
|
const date = ts ? moment(ts).format('LL') : null;
|
||||||
const unreadLine = { backgroundColor: themes[theme].dangerColor };
|
const unreadLine = { backgroundColor: themes[theme].dangerColor };
|
||||||
const unreadText = { color: themes[theme].dangerColor };
|
const unreadText = { color: themes[theme].dangerColor };
|
||||||
|
@ -63,10 +68,4 @@ const DateSeparator = React.memo(({ ts, unread, theme }) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
DateSeparator.propTypes = {
|
|
||||||
ts: PropTypes.instanceOf(Date),
|
|
||||||
unread: PropTypes.bool,
|
|
||||||
theme: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DateSeparator;
|
export default DateSeparator;
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
import { Observable, Subscription } from 'rxjs';
|
||||||
|
import Model from '@nozbe/watermelondb/Model';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
import RocketChat from '../../lib/rocketchat';
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
@ -51,20 +52,34 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class UploadProgress extends Component {
|
interface IRoomUploadProgressProps {
|
||||||
static propTypes = {
|
width: number;
|
||||||
width: PropTypes.number,
|
rid: string;
|
||||||
rid: PropTypes.string,
|
theme: string;
|
||||||
theme: PropTypes.string,
|
user: {
|
||||||
user: PropTypes.shape({
|
id: string;
|
||||||
id: PropTypes.string.isRequired,
|
username: string;
|
||||||
username: PropTypes.string.isRequired,
|
token: string;
|
||||||
token: PropTypes.string.isRequired
|
|
||||||
}),
|
|
||||||
baseUrl: PropTypes.string.isRequired
|
|
||||||
};
|
};
|
||||||
|
baseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
interface IItem {
|
||||||
|
name: string;
|
||||||
|
error: boolean;
|
||||||
|
progress: number;
|
||||||
|
path: string;
|
||||||
|
update(param: () => void): void;
|
||||||
|
destroyPermanently(): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class UploadProgress extends Component<IRoomUploadProgressProps, any> {
|
||||||
|
private mounted: boolean;
|
||||||
|
private ranInitialUploadCheck: boolean;
|
||||||
|
private uploadsSubscription?: Subscription;
|
||||||
|
private uploadsObservable?: Observable<Model>;
|
||||||
|
|
||||||
|
constructor(props: IRoomUploadProgressProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
this.ranInitialUploadCheck = false;
|
this.ranInitialUploadCheck = false;
|
||||||
|
@ -93,10 +108,11 @@ class UploadProgress extends Component {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
this.uploadsObservable = db.collections.get('uploads').query(Q.where('rid', rid)).observeWithColumns(['progress', 'error']);
|
this.uploadsObservable = db.collections.get('uploads').query(Q.where('rid', rid)).observeWithColumns(['progress', 'error']);
|
||||||
|
|
||||||
this.uploadsSubscription = this.uploadsObservable.subscribe(uploads => {
|
this.uploadsSubscription = this.uploadsObservable?.subscribe(uploads => {
|
||||||
if (this.mounted) {
|
if (this.mounted) {
|
||||||
this.setState({ uploads });
|
this.setState({ uploads });
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
this.state.uploads = uploads;
|
this.state.uploads = uploads;
|
||||||
}
|
}
|
||||||
if (!this.ranInitialUploadCheck) {
|
if (!this.ranInitialUploadCheck) {
|
||||||
|
@ -108,7 +124,7 @@ class UploadProgress extends Component {
|
||||||
uploadCheck = () => {
|
uploadCheck = () => {
|
||||||
this.ranInitialUploadCheck = true;
|
this.ranInitialUploadCheck = true;
|
||||||
const { uploads } = this.state;
|
const { uploads } = this.state;
|
||||||
uploads.forEach(async u => {
|
uploads.forEach(async (u: IItem) => {
|
||||||
if (!RocketChat.isUploadActive(u.path)) {
|
if (!RocketChat.isUploadActive(u.path)) {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
|
@ -124,7 +140,7 @@ class UploadProgress extends Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteUpload = async item => {
|
deleteUpload = async (item: IItem) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
|
@ -135,7 +151,7 @@ class UploadProgress extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
cancelUpload = async item => {
|
cancelUpload = async (item: { path: string }) => {
|
||||||
try {
|
try {
|
||||||
await RocketChat.cancelUpload(item);
|
await RocketChat.cancelUpload(item);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -143,7 +159,7 @@ class UploadProgress extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tryAgain = async item => {
|
tryAgain = async (item: IItem) => {
|
||||||
const { rid, baseUrl: server, user } = this.props;
|
const { rid, baseUrl: server, user } = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -159,7 +175,7 @@ class UploadProgress extends Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderItemContent = item => {
|
renderItemContent = (item: IItem) => {
|
||||||
const { width, theme } = this.props;
|
const { width, theme } = this.props;
|
||||||
|
|
||||||
if (!item.error) {
|
if (!item.error) {
|
||||||
|
@ -196,7 +212,7 @@ class UploadProgress extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: transform into stateless and update based on its own observable changes
|
// TODO: transform into stateless and update based on its own observable changes
|
||||||
renderItem = (item, index) => {
|
renderItem = (item: IItem, index: number) => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -217,7 +233,7 @@ class UploadProgress extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { uploads } = this.state;
|
const { uploads } = this.state;
|
||||||
return <ScrollView style={styles.container}>{uploads.map((item, i) => this.renderItem(item, i))}</ScrollView>;
|
return <ScrollView style={styles.container}>{uploads.map((item: IItem, i: number) => this.renderItem(item, i))}</ScrollView>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React from 'react';
|
import React, { ForwardedRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { InteractionManager, Text, View } from 'react-native';
|
import { InteractionManager, Text, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import parse from 'url-parse';
|
import parse from 'url-parse';
|
||||||
|
@ -8,6 +7,8 @@ import * as Haptics from 'expo-haptics';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
import { NavigatorScreenParams, RouteProp } from '@react-navigation/core';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
import { replyBroadcast as replyBroadcastAction } from '../../actions/messages';
|
import { replyBroadcast as replyBroadcastAction } from '../../actions/messages';
|
||||||
|
@ -64,8 +65,29 @@ import JoinCode from './JoinCode';
|
||||||
import UploadProgress from './UploadProgress';
|
import UploadProgress from './UploadProgress';
|
||||||
import ReactionPicker from './ReactionPicker';
|
import ReactionPicker from './ReactionPicker';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
|
import { IRoom, TRoomModel, RoomType } from '../../definitions/IRoom';
|
||||||
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
|
import { IThread } from '../../definitions/IThread';
|
||||||
|
import { ISubscriptions } from '../../definitions/ISubscriptions';
|
||||||
|
import { ModalStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
|
||||||
const stateAttrsUpdate = [
|
type TStateAttrsUpdate =
|
||||||
|
| 'joined'
|
||||||
|
| 'lastOpen'
|
||||||
|
| 'reactionsModalVisible'
|
||||||
|
| 'canAutoTranslate'
|
||||||
|
| 'selectedMessage'
|
||||||
|
| 'loading'
|
||||||
|
| 'editing'
|
||||||
|
| 'replying'
|
||||||
|
| 'reacting'
|
||||||
|
| 'readOnly'
|
||||||
|
| 'member'
|
||||||
|
| 'showingBlockingLoader';
|
||||||
|
|
||||||
|
const stateAttrsUpdate: TStateAttrsUpdate[] = [
|
||||||
'joined',
|
'joined',
|
||||||
'lastOpen',
|
'lastOpen',
|
||||||
'reactionsModalVisible',
|
'reactionsModalVisible',
|
||||||
|
@ -79,7 +101,30 @@ const stateAttrsUpdate = [
|
||||||
'member',
|
'member',
|
||||||
'showingBlockingLoader'
|
'showingBlockingLoader'
|
||||||
];
|
];
|
||||||
const roomAttrsUpdate = [
|
|
||||||
|
type TRoomAttrsUpdate =
|
||||||
|
| 'f'
|
||||||
|
| 'ro'
|
||||||
|
| 'blocked'
|
||||||
|
| 'blocker'
|
||||||
|
| 'archived'
|
||||||
|
| 'tunread'
|
||||||
|
| 'muted'
|
||||||
|
| 'ignored'
|
||||||
|
| 'jitsiTimeout'
|
||||||
|
| 'announcement'
|
||||||
|
| 'sysMes'
|
||||||
|
| 'topic'
|
||||||
|
| 'name'
|
||||||
|
| 'fname'
|
||||||
|
| 'roles'
|
||||||
|
| 'bannerClosed'
|
||||||
|
| 'visitor'
|
||||||
|
| 'joinCodeRequired'
|
||||||
|
| 'teamMain'
|
||||||
|
| 'teamId';
|
||||||
|
|
||||||
|
const roomAttrsUpdate: TRoomAttrsUpdate[] = [
|
||||||
'f',
|
'f',
|
||||||
'ro',
|
'ro',
|
||||||
'blocked',
|
'blocked',
|
||||||
|
@ -102,52 +147,135 @@ const roomAttrsUpdate = [
|
||||||
'teamId'
|
'teamId'
|
||||||
];
|
];
|
||||||
|
|
||||||
class RoomView extends React.Component {
|
interface IRoomViewProps {
|
||||||
static propTypes = {
|
navigation: StackNavigationProp<ChatsStackParamList, 'RoomView'>;
|
||||||
navigation: PropTypes.object,
|
route: RouteProp<ChatsStackParamList, 'RoomView'>;
|
||||||
route: PropTypes.object,
|
user: {
|
||||||
user: PropTypes.shape({
|
id: string;
|
||||||
id: PropTypes.string.isRequired,
|
username: string;
|
||||||
username: PropTypes.string.isRequired,
|
token: string;
|
||||||
token: PropTypes.string.isRequired,
|
showMessageInMainThread: boolean;
|
||||||
showMessageInMainThread: PropTypes.bool
|
|
||||||
}),
|
|
||||||
appState: PropTypes.string,
|
|
||||||
useRealName: PropTypes.bool,
|
|
||||||
isAuthenticated: PropTypes.bool,
|
|
||||||
Message_GroupingPeriod: PropTypes.number,
|
|
||||||
Message_TimeFormat: PropTypes.string,
|
|
||||||
Message_Read_Receipt_Enabled: PropTypes.bool,
|
|
||||||
Hide_System_Messages: PropTypes.array,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
serverVersion: PropTypes.string,
|
|
||||||
customEmojis: PropTypes.object,
|
|
||||||
isMasterDetail: PropTypes.bool,
|
|
||||||
theme: PropTypes.string,
|
|
||||||
replyBroadcast: PropTypes.func,
|
|
||||||
width: PropTypes.number,
|
|
||||||
height: PropTypes.number,
|
|
||||||
insets: PropTypes.object
|
|
||||||
};
|
};
|
||||||
|
appState: string;
|
||||||
|
useRealName: boolean;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
Message_GroupingPeriod: number;
|
||||||
|
Message_TimeFormat: string;
|
||||||
|
Message_Read_Receipt_Enabled: boolean;
|
||||||
|
Hide_System_Messages: [];
|
||||||
|
baseUrl: string;
|
||||||
|
serverVersion: string;
|
||||||
|
customEmojis: { [key: string]: string };
|
||||||
|
isMasterDetail: boolean;
|
||||||
|
theme: string;
|
||||||
|
replyBroadcast(message: string): void;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
insets: {
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
interface IRoomViewState {
|
||||||
|
joined: boolean;
|
||||||
|
room: TRoomModel;
|
||||||
|
roomUpdate: Partial<IRoom>;
|
||||||
|
member: {
|
||||||
|
statusText?: string;
|
||||||
|
};
|
||||||
|
lastOpen: Date | null;
|
||||||
|
reactionsModalVisible: boolean;
|
||||||
|
selectedMessage: {};
|
||||||
|
canAutoTranslate: boolean;
|
||||||
|
loading: boolean;
|
||||||
|
showingBlockingLoader: boolean;
|
||||||
|
editing: boolean;
|
||||||
|
replying: boolean;
|
||||||
|
replyWithMention: boolean;
|
||||||
|
reacting: boolean;
|
||||||
|
readOnly: boolean;
|
||||||
|
unreadsCount: number | null;
|
||||||
|
roomUserId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoomItem {
|
||||||
|
id: string;
|
||||||
|
t: string;
|
||||||
|
rid: string;
|
||||||
|
tmid?: string;
|
||||||
|
ts: Date;
|
||||||
|
status?: string;
|
||||||
|
u?: { _id: string };
|
||||||
|
loaderItem: {
|
||||||
|
t: string;
|
||||||
|
ts: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INavToThread {
|
||||||
|
id?: string;
|
||||||
|
tmsg?: string;
|
||||||
|
t?: string;
|
||||||
|
e2e?: string;
|
||||||
|
tmid?: string;
|
||||||
|
tlm?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IBlockAction {
|
||||||
|
actionId: string;
|
||||||
|
appId: string;
|
||||||
|
value: string;
|
||||||
|
blockId: string;
|
||||||
|
rid: string;
|
||||||
|
mid: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomView extends React.Component<IRoomViewProps, IRoomViewState> {
|
||||||
|
private rid: string;
|
||||||
|
private t: RoomType;
|
||||||
|
private tmid?: string;
|
||||||
|
private jumpToMessageId?: string;
|
||||||
|
private jumpToThreadId?: string;
|
||||||
|
private messagebox: React.RefObject<typeof MessageBox>;
|
||||||
|
private list: React.RefObject<List>;
|
||||||
|
private joinCode?: React.ForwardedRef<typeof JoinCode>;
|
||||||
|
private flatList: any;
|
||||||
|
private mounted: boolean;
|
||||||
|
private sub?: RoomClass;
|
||||||
|
private offset?: number;
|
||||||
|
private didMountInteraction?: { cancel: () => void };
|
||||||
|
|
||||||
|
private willBlurListener?: { remove(): void };
|
||||||
|
private subSubscription?: { unsubscribe(): void };
|
||||||
|
private queryUnreads?: { unsubscribe(): void };
|
||||||
|
private retryInit?: number;
|
||||||
|
private retryInitTimeout?: ReturnType<typeof setTimeout>;
|
||||||
|
private retryFindCount?: number;
|
||||||
|
private retryFindTimeout?: ReturnType<typeof setTimeout>;
|
||||||
|
private messageErrorActions?: React.ForwardedRef<typeof MessageErrorActions>;
|
||||||
|
private messageActions?: React.ForwardedRef<typeof MessageActions>;
|
||||||
|
|
||||||
|
constructor(props: IRoomViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
console.time(`${this.constructor.name} init`);
|
console.time(`${this.constructor.name} init`);
|
||||||
console.time(`${this.constructor.name} mount`);
|
console.time(`${this.constructor.name} mount`);
|
||||||
this.rid = props.route.params?.rid;
|
this.rid = props.route.params.rid;
|
||||||
this.t = props.route.params?.t;
|
this.t = props.route.params.t;
|
||||||
this.tmid = props.route.params?.tmid;
|
this.tmid = props.route.params?.tmid;
|
||||||
const selectedMessage = props.route.params?.message;
|
const selectedMessage = props.route.params?.message;
|
||||||
const name = props.route.params?.name;
|
const name = props.route.params?.name;
|
||||||
const fname = props.route.params?.fname;
|
const fname = props.route.params?.fname;
|
||||||
const prid = props.route.params?.prid;
|
const prid = props.route.params?.prid;
|
||||||
const room = props.route.params?.room ?? {
|
const room =
|
||||||
rid: this.rid,
|
props.route.params?.room ??
|
||||||
t: this.t,
|
({
|
||||||
name,
|
rid: this.rid,
|
||||||
fname,
|
t: this.t,
|
||||||
prid
|
name,
|
||||||
};
|
fname,
|
||||||
|
prid
|
||||||
|
} as TRoomModel);
|
||||||
this.jumpToMessageId = props.route.params?.jumpToMessageId;
|
this.jumpToMessageId = props.route.params?.jumpToMessageId;
|
||||||
this.jumpToThreadId = props.route.params?.jumpToThreadId;
|
this.jumpToThreadId = props.route.params?.jumpToThreadId;
|
||||||
const roomUserId = props.route.params?.roomUserId ?? RocketChat.getUidDirectMessage(room);
|
const roomUserId = props.route.params?.roomUserId ?? RocketChat.getUidDirectMessage(room);
|
||||||
|
@ -172,7 +300,7 @@ class RoomView extends React.Component {
|
||||||
};
|
};
|
||||||
this.setHeader();
|
this.setHeader();
|
||||||
|
|
||||||
if (room && room.observe) {
|
if (room && room.observe()) {
|
||||||
this.observeRoom(room);
|
this.observeRoom(room);
|
||||||
} else if (this.rid) {
|
} else if (this.rid) {
|
||||||
this.findAndObserveRoom(this.rid);
|
this.findAndObserveRoom(this.rid);
|
||||||
|
@ -224,7 +352,7 @@ class RoomView extends React.Component {
|
||||||
console.timeEnd(`${this.constructor.name} mount`);
|
console.timeEnd(`${this.constructor.name} mount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps: IRoomViewProps, nextState: IRoomViewState) {
|
||||||
const { state } = this;
|
const { state } = this;
|
||||||
const { roomUpdate, member } = state;
|
const { roomUpdate, member } = state;
|
||||||
const { appState, theme, insets, route } = this.props;
|
const { appState, theme, insets, route } = this.props;
|
||||||
|
@ -250,7 +378,7 @@ class RoomView extends React.Component {
|
||||||
return roomAttrsUpdate.some(key => !dequal(nextState.roomUpdate[key], roomUpdate[key]));
|
return roomAttrsUpdate.some(key => !dequal(nextState.roomUpdate[key], roomUpdate[key]));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
componentDidUpdate(prevProps: IRoomViewProps, prevState: IRoomViewState) {
|
||||||
const { roomUpdate } = this.state;
|
const { roomUpdate } = this.state;
|
||||||
const { appState, insets, route } = this.props;
|
const { appState, insets, route } = this.props;
|
||||||
|
|
||||||
|
@ -304,7 +432,8 @@ class RoomView extends React.Component {
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
if (!editing && this.messagebox && this.messagebox.current) {
|
if (!editing && this.messagebox && this.messagebox.current) {
|
||||||
const { text } = this.messagebox.current;
|
const { text } = this.messagebox.current;
|
||||||
let obj;
|
let obj = room; // TODO - test the threadsCollection.find return to change this any;
|
||||||
|
|
||||||
if (this.tmid) {
|
if (this.tmid) {
|
||||||
try {
|
try {
|
||||||
const threadsCollection = db.get('threads');
|
const threadsCollection = db.get('threads');
|
||||||
|
@ -312,13 +441,13 @@ class RoomView extends React.Component {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
obj = room;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
try {
|
try {
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
await obj.update(r => {
|
await obj.update(r => {
|
||||||
|
// TODO - change this any
|
||||||
r.draftMessage = text;
|
r.draftMessage = text;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -360,7 +489,7 @@ class RoomView extends React.Component {
|
||||||
const prid = room?.prid;
|
const prid = room?.prid;
|
||||||
const isGroupChat = RocketChat.isGroupChat(room);
|
const isGroupChat = RocketChat.isGroupChat(room);
|
||||||
let title = route.params?.name;
|
let title = route.params?.name;
|
||||||
let parentTitle;
|
let parentTitle: string;
|
||||||
if ((room.id || room.rid) && !tmid) {
|
if ((room.id || room.rid) && !tmid) {
|
||||||
title = RocketChat.getRoomTitle(room);
|
title = RocketChat.getRoomTitle(room);
|
||||||
}
|
}
|
||||||
|
@ -397,7 +526,8 @@ class RoomView extends React.Component {
|
||||||
headerLeft: () => (
|
headerLeft: () => (
|
||||||
<LeftButtons
|
<LeftButtons
|
||||||
tmid={tmid}
|
tmid={tmid}
|
||||||
unreadsCount={unreadsCount}
|
// TODO - remove this type after craete the Room state
|
||||||
|
unreadsCount={unreadsCount as number & string}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
|
@ -430,11 +560,10 @@ class RoomView extends React.Component {
|
||||||
<RightButtons
|
<RightButtons
|
||||||
rid={rid}
|
rid={rid}
|
||||||
tmid={tmid}
|
tmid={tmid}
|
||||||
teamId={teamId}
|
teamId={teamId!}
|
||||||
teamMain={teamMain}
|
|
||||||
joined={joined}
|
joined={joined}
|
||||||
t={t}
|
t={t}
|
||||||
encrypted={encrypted}
|
encrypted={encrypted!}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
toggleFollowThread={this.toggleFollowThread}
|
toggleFollowThread={this.toggleFollowThread}
|
||||||
/>
|
/>
|
||||||
|
@ -442,7 +571,7 @@ class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
goRoomActionsView = screen => {
|
goRoomActionsView = (screen?: 'SearchMessagesView' | 'RoomActionsView') => {
|
||||||
logEvent(events.ROOM_GO_RA);
|
logEvent(events.ROOM_GO_RA);
|
||||||
const { room, member, joined } = this.state;
|
const { room, member, joined } = this.state;
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
|
@ -493,7 +622,8 @@ class RoomView extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
this.setLastOpen(null);
|
this.setLastOpen(null);
|
||||||
}
|
}
|
||||||
RoomServices.readMessages(room.rid, newLastOpen, true).catch(e => console.log(e));
|
// RoomServices.readMessages(room.rid, newLastOpen, true).catch(e => console.log(e)); this function receives true automatic
|
||||||
|
RoomServices.readMessages(room.rid, newLastOpen).catch(e => console.log(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +633,7 @@ class RoomView extends React.Component {
|
||||||
this.setState({ canAutoTranslate, member, loading: false });
|
this.setState({ canAutoTranslate, member, loading: false });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
this.retryInit = this.retryInit + 1 || 1;
|
this.retryInit = this.retryInit! + 1 || 1;
|
||||||
if (this.retryInit <= 1) {
|
if (this.retryInit <= 1) {
|
||||||
this.retryInitTimeout = setTimeout(() => {
|
this.retryInitTimeout = setTimeout(() => {
|
||||||
this.init();
|
this.init();
|
||||||
|
@ -533,7 +663,7 @@ class RoomView extends React.Component {
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
findAndObserveRoom = async rid => {
|
findAndObserveRoom = async (rid: string) => {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const subCollection = await db.get('subscriptions');
|
const subCollection = await db.get('subscriptions');
|
||||||
|
@ -551,7 +681,7 @@ class RoomView extends React.Component {
|
||||||
if (this.rid) {
|
if (this.rid) {
|
||||||
// We navigate to RoomView before the Room is inserted to the local db
|
// We navigate to RoomView before the Room is inserted to the local db
|
||||||
// So we retry just to make sure we have the right content
|
// So we retry just to make sure we have the right content
|
||||||
this.retryFindCount = this.retryFindCount + 1 || 1;
|
this.retryFindCount = this.retryFindCount! + 1 || 1;
|
||||||
if (this.retryFindCount <= 3) {
|
if (this.retryFindCount <= 3) {
|
||||||
this.retryFindTimeout = setTimeout(() => {
|
this.retryFindTimeout = setTimeout(() => {
|
||||||
this.findAndObserveRoom(rid);
|
this.findAndObserveRoom(rid);
|
||||||
|
@ -569,27 +699,32 @@ class RoomView extends React.Component {
|
||||||
delete this.sub;
|
delete this.sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
observeRoom = room => {
|
observeRoom = (room: TRoomModel) => {
|
||||||
const observable = room.observe();
|
if (room.observe) {
|
||||||
this.subSubscription = observable.subscribe(changes => {
|
const observable = room.observe();
|
||||||
const roomUpdate = roomAttrsUpdate.reduce((ret, attr) => {
|
this.subSubscription = observable.subscribe(changes => {
|
||||||
ret[attr] = changes[attr];
|
const roomUpdate = roomAttrsUpdate.reduce((ret: any, attr) => {
|
||||||
return ret;
|
ret[attr] = changes[attr];
|
||||||
}, {});
|
return ret;
|
||||||
if (this.mounted) {
|
}, {});
|
||||||
this.internalSetState({ room: changes, roomUpdate });
|
if (this.mounted) {
|
||||||
} else {
|
this.internalSetState({ room: changes, roomUpdate });
|
||||||
this.state.room = changes;
|
} else {
|
||||||
this.state.roomUpdate = roomUpdate;
|
// @ts-ignore
|
||||||
}
|
this.state.room = changes;
|
||||||
});
|
// @ts-ignore
|
||||||
|
this.state.roomUpdate = roomUpdate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
errorActionsShow = message => {
|
errorActionsShow = (message: string) => {
|
||||||
|
// @ts-ignore
|
||||||
this.messageErrorActions?.showMessageErrorActions(message);
|
this.messageErrorActions?.showMessageErrorActions(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
onEditInit = message => {
|
onEditInit = (message: { id: string; subscription: { id: string }; attachments: IAttachment[]; msg: string }) => {
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
id: message.id,
|
id: message.id,
|
||||||
subscription: {
|
subscription: {
|
||||||
|
@ -604,7 +739,7 @@ class RoomView extends React.Component {
|
||||||
this.setState({ selectedMessage: {}, editing: false });
|
this.setState({ selectedMessage: {}, editing: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onEditRequest = async message => {
|
onEditRequest = async (message: string) => {
|
||||||
this.setState({ selectedMessage: {}, editing: false });
|
this.setState({ selectedMessage: {}, editing: false });
|
||||||
try {
|
try {
|
||||||
await RocketChat.editMessage(message);
|
await RocketChat.editMessage(message);
|
||||||
|
@ -613,7 +748,7 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onReplyInit = (message, mention) => {
|
onReplyInit = (message: IMessage, mention: boolean) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedMessage: message,
|
selectedMessage: message,
|
||||||
replying: true,
|
replying: true,
|
||||||
|
@ -625,7 +760,7 @@ class RoomView extends React.Component {
|
||||||
this.setState({ selectedMessage: {}, replying: false, replyWithMention: false });
|
this.setState({ selectedMessage: {}, replying: false, replyWithMention: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onReactionInit = message => {
|
onReactionInit = (message: string) => {
|
||||||
this.setState({ selectedMessage: message, reacting: true });
|
this.setState({ selectedMessage: message, reacting: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -633,16 +768,17 @@ class RoomView extends React.Component {
|
||||||
this.setState({ selectedMessage: {}, reacting: false });
|
this.setState({ selectedMessage: {}, reacting: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
onMessageLongPress = message => {
|
onMessageLongPress = (message: string) => {
|
||||||
|
// @ts-ignore
|
||||||
this.messageActions?.showMessageActions(message);
|
this.messageActions?.showMessageActions(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
showAttachment = attachment => {
|
showAttachment = (attachment: IAttachment) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('AttachmentView', { attachment });
|
navigation.navigate('AttachmentView', { attachment });
|
||||||
};
|
};
|
||||||
|
|
||||||
onReactionPress = async (shortname, messageId) => {
|
onReactionPress = async (shortname: string, messageId: string) => {
|
||||||
try {
|
try {
|
||||||
await RocketChat.setReaction(shortname, messageId);
|
await RocketChat.setReaction(shortname, messageId);
|
||||||
this.onReactionClose();
|
this.onReactionClose();
|
||||||
|
@ -652,7 +788,7 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onReactionLongPress = message => {
|
onReactionLongPress = (message: string) => {
|
||||||
this.setState({ selectedMessage: message, reactionsModalVisible: true });
|
this.setState({ selectedMessage: message, reactionsModalVisible: true });
|
||||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
};
|
};
|
||||||
|
@ -665,7 +801,7 @@ class RoomView extends React.Component {
|
||||||
logEvent(events.ROOM_ENCRYPTED_PRESS);
|
logEvent(events.ROOM_ENCRYPTED_PRESS);
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
|
|
||||||
const screen = { screen: 'E2EHowItWorksView', params: { showCloseModal: true } };
|
const screen: NavigatorScreenParams<ModalStackParamList> = { screen: 'E2EHowItWorksView', params: { showCloseModal: true } };
|
||||||
|
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
return navigation.navigate('ModalStackNavigator', screen);
|
return navigation.navigate('ModalStackNavigator', screen);
|
||||||
|
@ -674,13 +810,13 @@ class RoomView extends React.Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
onDiscussionPress = debounce(
|
onDiscussionPress = debounce(
|
||||||
item => {
|
(item: IThread) => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.push('RoomView', {
|
navigation.push('RoomView', {
|
||||||
rid: item.drid,
|
rid: item.drid,
|
||||||
prid: item.rid,
|
prid: item.rid,
|
||||||
name: item.msg,
|
name: item.msg,
|
||||||
t: 'p'
|
t: RoomType.GROUP
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
|
@ -695,7 +831,7 @@ class RoomView extends React.Component {
|
||||||
.query(Q.where('archived', false), Q.where('open', true), Q.where('rid', Q.notEq(this.rid)))
|
.query(Q.where('archived', false), Q.where('open', true), Q.where('rid', Q.notEq(this.rid)))
|
||||||
.observeWithColumns(['unread']);
|
.observeWithColumns(['unread']);
|
||||||
|
|
||||||
this.queryUnreads = observable.subscribe(data => {
|
this.queryUnreads = observable.subscribe((data: ISubscriptions[]) => {
|
||||||
const { unreadsCount } = this.state;
|
const { unreadsCount } = this.state;
|
||||||
const newUnreadsCount = data.filter(s => s.unread > 0).reduce((a, b) => a + (b.unread || 0), 0);
|
const newUnreadsCount = data.filter(s => s.unread > 0).reduce((a, b) => a + (b.unread || 0), 0);
|
||||||
if (unreadsCount !== newUnreadsCount) {
|
if (unreadsCount !== newUnreadsCount) {
|
||||||
|
@ -704,9 +840,9 @@ class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onThreadPress = debounce(item => this.navToThread(item), 1000, true);
|
onThreadPress = debounce((item: IRoomItem) => this.navToThread(item), 1000, true);
|
||||||
|
|
||||||
shouldNavigateToRoom = message => {
|
shouldNavigateToRoom = (message: { tmid: string; rid: string }) => {
|
||||||
if (message.tmid && message.tmid === this.tmid) {
|
if (message.tmid && message.tmid === this.tmid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -716,7 +852,7 @@ class RoomView extends React.Component {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
jumpToMessageByUrl = async messageUrl => {
|
jumpToMessageByUrl = async (messageUrl: string) => {
|
||||||
if (!messageUrl) {
|
if (!messageUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -732,10 +868,10 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
jumpToMessage = async messageId => {
|
jumpToMessage = async (messageId?: string) => {
|
||||||
try {
|
try {
|
||||||
this.setState({ showingBlockingLoader: true });
|
this.setState({ showingBlockingLoader: true });
|
||||||
const message = await RoomServices.getMessageInfo(messageId);
|
const message = await RoomServices.getMessageInfo(messageId!);
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return;
|
return;
|
||||||
|
@ -755,8 +891,8 @@ class RoomView extends React.Component {
|
||||||
if (message.fromServer && !message.tmid) {
|
if (message.fromServer && !message.tmid) {
|
||||||
await RocketChat.loadSurroundingMessages({ messageId, rid: this.rid });
|
await RocketChat.loadSurroundingMessages({ messageId, rid: this.rid });
|
||||||
}
|
}
|
||||||
await Promise.race([this.list.current.jumpToMessage(message.id), new Promise(res => setTimeout(res, 5000))]);
|
await Promise.race([this.list.current?.jumpToMessage(message.id), new Promise(res => setTimeout(res, 5000))]);
|
||||||
this.list.current.cancelJumpToMessage();
|
this.list.current?.cancelJumpToMessage();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
|
@ -765,7 +901,7 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
replyBroadcast = message => {
|
replyBroadcast = (message: string) => {
|
||||||
const { replyBroadcast } = this.props;
|
const { replyBroadcast } = this.props;
|
||||||
replyBroadcast(message);
|
replyBroadcast(message);
|
||||||
};
|
};
|
||||||
|
@ -775,7 +911,7 @@ class RoomView extends React.Component {
|
||||||
EventEmitter.removeListener('connected', this.handleConnected);
|
EventEmitter.removeListener('connected', this.handleConnected);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRoomRemoved = ({ rid }) => {
|
handleRoomRemoved = ({ rid }: { rid: string }) => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
if (rid === this.rid) {
|
if (rid === this.rid) {
|
||||||
Navigation.navigate('RoomsListView');
|
Navigation.navigate('RoomsListView');
|
||||||
|
@ -784,14 +920,15 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
internalSetState = (...args) => {
|
internalSetState = (...args: any) => {
|
||||||
if (!this.mounted) {
|
if (!this.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// @ts-ignore
|
||||||
this.setState(...args);
|
this.setState(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
sendMessage = (message, tmid, tshow) => {
|
sendMessage = (message: string, tmid: string, tshow: string) => {
|
||||||
logEvent(events.ROOM_SEND_MESSAGE);
|
logEvent(events.ROOM_SEND_MESSAGE);
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user, tshow).then(() => {
|
RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user, tshow).then(() => {
|
||||||
|
@ -803,7 +940,7 @@ class RoomView extends React.Component {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getCustomEmoji = name => {
|
getCustomEmoji = (name: string) => {
|
||||||
const { customEmojis } = this.props;
|
const { customEmojis } = this.props;
|
||||||
const emoji = customEmojis[name];
|
const emoji = customEmojis[name];
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
|
@ -812,7 +949,7 @@ class RoomView extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
setLastOpen = lastOpen => this.setState({ lastOpen });
|
setLastOpen = (lastOpen: Date | null) => this.setState({ lastOpen });
|
||||||
|
|
||||||
onJoin = () => {
|
onJoin = () => {
|
||||||
this.internalSetState({
|
this.internalSetState({
|
||||||
|
@ -831,7 +968,8 @@ class RoomView extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
const { joinCodeRequired } = room;
|
const { joinCodeRequired } = room;
|
||||||
if (joinCodeRequired) {
|
if (joinCodeRequired) {
|
||||||
this.joinCode.current?.show();
|
// @ts-ignore
|
||||||
|
this.joinCode?.current?.show();
|
||||||
} else {
|
} else {
|
||||||
await RocketChat.joinRoom(this.rid, null, this.t);
|
await RocketChat.joinRoom(this.rid, null, this.t);
|
||||||
this.onJoin();
|
this.onJoin();
|
||||||
|
@ -842,9 +980,9 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getThreadName = (tmid, messageId) => getThreadName(this.rid, tmid, messageId);
|
getThreadName = (tmid: string, messageId?: string) => getThreadName(this.rid, tmid, messageId);
|
||||||
|
|
||||||
toggleFollowThread = async (isFollowingThread, tmid) => {
|
toggleFollowThread = async (isFollowingThread: boolean, tmid: string) => {
|
||||||
try {
|
try {
|
||||||
await RocketChat.toggleFollowMessage(tmid ?? this.tmid, !isFollowingThread);
|
await RocketChat.toggleFollowMessage(tmid ?? this.tmid, !isFollowingThread);
|
||||||
EventEmitter.emit(LISTENER, { message: isFollowingThread ? I18n.t('Unfollowed_thread') : I18n.t('Following_thread') });
|
EventEmitter.emit(LISTENER, { message: isFollowingThread ? I18n.t('Unfollowed_thread') : I18n.t('Following_thread') });
|
||||||
|
@ -853,13 +991,13 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getBadgeColor = messageId => {
|
getBadgeColor = (messageId?: string) => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
return getBadgeColor({ subscription: room, theme, messageId });
|
return getBadgeColor({ subscription: room, theme, messageId });
|
||||||
};
|
};
|
||||||
|
|
||||||
navToRoomInfo = navParam => {
|
navToRoomInfo = (navParam: ChatsStackParamList['RoomInfoView']) => {
|
||||||
const { navigation, user, isMasterDetail } = this.props;
|
const { navigation, user, isMasterDetail } = this.props;
|
||||||
logEvent(events[`ROOM_GO_${navParam.t === 'd' ? 'USER' : 'ROOM'}_INFO`]);
|
logEvent(events[`ROOM_GO_${navParam.t === 'd' ? 'USER' : 'ROOM'}_INFO`]);
|
||||||
if (navParam.rid === user.id) {
|
if (navParam.rid === user.id) {
|
||||||
|
@ -873,7 +1011,7 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
navToThread = async item => {
|
navToThread = async (item: INavToThread) => {
|
||||||
const { roomUserId } = this.state;
|
const { roomUserId } = this.state;
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
|
|
||||||
|
@ -894,7 +1032,7 @@ class RoomView extends React.Component {
|
||||||
rid: this.rid,
|
rid: this.rid,
|
||||||
tmid: item.tmid,
|
tmid: item.tmid,
|
||||||
name,
|
name,
|
||||||
t: 'thread',
|
t: RoomType.THREAD,
|
||||||
roomUserId,
|
roomUserId,
|
||||||
jumpToMessageId: item.id
|
jumpToMessageId: item.id
|
||||||
});
|
});
|
||||||
|
@ -905,15 +1043,15 @@ class RoomView extends React.Component {
|
||||||
rid: this.rid,
|
rid: this.rid,
|
||||||
tmid: item.id,
|
tmid: item.id,
|
||||||
name: makeThreadName(item),
|
name: makeThreadName(item),
|
||||||
t: 'thread',
|
t: RoomType.THREAD,
|
||||||
roomUserId
|
roomUserId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
navToRoom = async message => {
|
navToRoom = async (message: { rid: string; id: string }) => {
|
||||||
const { navigation, isMasterDetail } = this.props;
|
const { navigation, isMasterDetail } = this.props;
|
||||||
const roomInfo = await getRoomInfo(message.rid);
|
const roomInfo = (await getRoomInfo(message.rid)) as IRoom;
|
||||||
return goRoom({
|
return goRoom({
|
||||||
item: roomInfo,
|
item: roomInfo,
|
||||||
isMasterDetail,
|
isMasterDetail,
|
||||||
|
@ -932,12 +1070,12 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleCommands = ({ event }) => {
|
handleCommands = ({ event }: { event: { input: string } }) => {
|
||||||
if (this.rid) {
|
if (this.rid) {
|
||||||
const { input } = event;
|
const { input } = event;
|
||||||
if (handleCommandScroll(event)) {
|
if (handleCommandScroll(event)) {
|
||||||
const offset = input === 'UIKeyInputUpArrow' ? 100 : -100;
|
const offset: number = input === 'UIKeyInputUpArrow' ? 100 : -100;
|
||||||
this.offset += offset;
|
this.offset! += offset;
|
||||||
this.flatList?.scrollToOffset({ offset: this.offset });
|
this.flatList?.scrollToOffset({ offset: this.offset });
|
||||||
} else if (handleCommandRoomActions(event)) {
|
} else if (handleCommandRoomActions(event)) {
|
||||||
this.goRoomActionsView();
|
this.goRoomActionsView();
|
||||||
|
@ -945,14 +1083,14 @@ class RoomView extends React.Component {
|
||||||
this.goRoomActionsView('SearchMessagesView');
|
this.goRoomActionsView('SearchMessagesView');
|
||||||
} else if (handleCommandReplyLatest(event)) {
|
} else if (handleCommandReplyLatest(event)) {
|
||||||
if (this.list && this.list.current) {
|
if (this.list && this.list.current) {
|
||||||
const message = this.list.current.getLastMessage();
|
const message = this.list.current.getLastMessage() as IMessage;
|
||||||
this.onReplyInit(message, false);
|
this.onReplyInit(message, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
blockAction = ({ actionId, appId, value, blockId, rid, mid }) =>
|
blockAction = ({ actionId, appId, value, blockId, rid, mid }: IBlockAction) =>
|
||||||
RocketChat.triggerBlockAction({
|
RocketChat.triggerBlockAction({
|
||||||
blockId,
|
blockId,
|
||||||
actionId,
|
actionId,
|
||||||
|
@ -971,7 +1109,7 @@ class RoomView extends React.Component {
|
||||||
try {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
await db.action(async () => {
|
await db.action(async () => {
|
||||||
await room.update(r => {
|
await room.update((r: IRoom) => {
|
||||||
r.bannerClosed = true;
|
r.bannerClosed = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -980,12 +1118,12 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isIgnored = message => {
|
isIgnored = (message: IRoomItem) => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
return room?.ignored?.includes?.(message?.u?._id) ?? false;
|
return room?.ignored?.includes?.(message?.u?._id) ?? false;
|
||||||
};
|
};
|
||||||
|
|
||||||
onLoadMoreMessages = loaderItem =>
|
onLoadMoreMessages = (loaderItem: IRoomItem) =>
|
||||||
RoomServices.getMoreMessages({
|
RoomServices.getMoreMessages({
|
||||||
rid: this.rid,
|
rid: this.rid,
|
||||||
tmid: this.tmid,
|
tmid: this.tmid,
|
||||||
|
@ -993,7 +1131,7 @@ class RoomView extends React.Component {
|
||||||
loaderItem
|
loaderItem
|
||||||
});
|
});
|
||||||
|
|
||||||
renderItem = (item, previousItem, highlightedMessage) => {
|
renderItem = (item: IRoomItem, previousItem: IRoomItem, highlightedMessage: string) => {
|
||||||
const { room, lastOpen, canAutoTranslate } = this.state;
|
const { room, lastOpen, canAutoTranslate } = this.state;
|
||||||
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
|
const { user, Message_GroupingPeriod, Message_TimeFormat, useRealName, baseUrl, Message_Read_Receipt_Enabled, theme } =
|
||||||
this.props;
|
this.props;
|
||||||
|
@ -1004,7 +1142,7 @@ class RoomView extends React.Component {
|
||||||
dateSeparator = item.ts;
|
dateSeparator = item.ts;
|
||||||
showUnreadSeparator = moment(item.ts).isAfter(lastOpen);
|
showUnreadSeparator = moment(item.ts).isAfter(lastOpen);
|
||||||
} else {
|
} else {
|
||||||
showUnreadSeparator = lastOpen && moment(item.ts).isSameOrAfter(lastOpen) && moment(previousItem.ts).isBefore(lastOpen);
|
showUnreadSeparator = lastOpen! && moment(item.ts).isSameOrAfter(lastOpen) && moment(previousItem.ts).isBefore(lastOpen);
|
||||||
if (!moment(item.ts).isSame(previousItem.ts, 'day')) {
|
if (!moment(item.ts).isSame(previousItem.ts, 'day')) {
|
||||||
dateSeparator = item.ts;
|
dateSeparator = item.ts;
|
||||||
}
|
}
|
||||||
|
@ -1150,7 +1288,7 @@ class RoomView extends React.Component {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MessageActions
|
<MessageActions
|
||||||
ref={ref => (this.messageActions = ref)}
|
ref={(ref: ForwardedRef<typeof MessageActions>) => (this.messageActions = ref)}
|
||||||
tmid={this.tmid}
|
tmid={this.tmid}
|
||||||
room={room}
|
room={room}
|
||||||
user={user}
|
user={user}
|
||||||
|
@ -1160,7 +1298,10 @@ class RoomView extends React.Component {
|
||||||
onReactionPress={this.onReactionPress}
|
onReactionPress={this.onReactionPress}
|
||||||
isReadOnly={readOnly}
|
isReadOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
<MessageErrorActions ref={ref => (this.messageErrorActions = ref)} tmid={this.tmid} />
|
<MessageErrorActions
|
||||||
|
ref={(ref: ForwardedRef<typeof MessageErrorActions>) => (this.messageErrorActions = ref)}
|
||||||
|
tmid={this.tmid}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1175,7 +1316,6 @@ class RoomView extends React.Component {
|
||||||
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='room-view'>
|
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='room-view'>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<Banner
|
<Banner
|
||||||
rid={rid}
|
|
||||||
title={I18n.t('Announcement')}
|
title={I18n.t('Announcement')}
|
||||||
text={announcement}
|
text={announcement}
|
||||||
bannerClosed={bannerClosed}
|
bannerClosed={bannerClosed}
|
||||||
|
@ -1186,7 +1326,6 @@ class RoomView extends React.Component {
|
||||||
ref={this.list}
|
ref={this.list}
|
||||||
listRef={this.flatList}
|
listRef={this.flatList}
|
||||||
rid={rid}
|
rid={rid}
|
||||||
t={t}
|
|
||||||
tmid={this.tmid}
|
tmid={this.tmid}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
tunread={room?.tunread}
|
tunread={room?.tunread}
|
||||||
|
@ -1225,7 +1364,7 @@ class RoomView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: any) => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
isMasterDetail: state.app.isMasterDetail,
|
||||||
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
|
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background',
|
||||||
|
@ -1240,8 +1379,8 @@ const mapStateToProps = state => ({
|
||||||
Hide_System_Messages: state.settings.Hide_System_Messages
|
Hide_System_Messages: state.settings.Hide_System_Messages
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
replyBroadcast: message => dispatch(replyBroadcastAction(message))
|
replyBroadcast: (message: any) => dispatch(replyBroadcastAction(message))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomView))));
|
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomView))));
|
|
@ -2,7 +2,7 @@ import { getMessageById } from '../../../lib/database/services/Message';
|
||||||
import { getThreadMessageById } from '../../../lib/database/services/ThreadMessage';
|
import { getThreadMessageById } from '../../../lib/database/services/ThreadMessage';
|
||||||
import getSingleMessage from '../../../lib/methods/getSingleMessage';
|
import getSingleMessage from '../../../lib/methods/getSingleMessage';
|
||||||
|
|
||||||
const getMessageInfo = async messageId => {
|
const getMessageInfo = async (messageId: string) => {
|
||||||
let result;
|
let result;
|
||||||
result = await getMessageById(messageId);
|
result = await getMessageById(messageId);
|
||||||
if (result) {
|
if (result) {
|
|
@ -1,10 +1,10 @@
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
import { IRoom } from '../../../definitions/IRoom';
|
||||||
|
|
||||||
const getMessages = room => {
|
const getMessages = (room: IRoom): Promise<void> => {
|
||||||
if (room.lastOpen) {
|
if (room.lastOpen) {
|
||||||
return RocketChat.loadMissedMessages(room);
|
return RocketChat.loadMissedMessages(room);
|
||||||
} else {
|
|
||||||
return RocketChat.loadMessagesForRoom(room);
|
|
||||||
}
|
}
|
||||||
|
return RocketChat.loadMessagesForRoom(room);
|
||||||
};
|
};
|
||||||
export default getMessages;
|
export default getMessages;
|
|
@ -5,7 +5,17 @@ import {
|
||||||
} from '../../../constants/messageTypeLoad';
|
} from '../../../constants/messageTypeLoad';
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
|
||||||
const getMoreMessages = ({ rid, t, tmid, loaderItem }) => {
|
interface IGetMoreMessages {
|
||||||
|
rid: string;
|
||||||
|
t?: string;
|
||||||
|
tmid?: string;
|
||||||
|
loaderItem: {
|
||||||
|
t: string;
|
||||||
|
ts: Date;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMoreMessages = ({ rid, t, tmid, loaderItem }: IGetMoreMessages) => {
|
||||||
if ([MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK].includes(loaderItem.t)) {
|
if ([MESSAGE_TYPE_LOAD_MORE, MESSAGE_TYPE_LOAD_PREVIOUS_CHUNK].includes(loaderItem.t)) {
|
||||||
return RocketChat.loadMessagesForRoom({
|
return RocketChat.loadMessagesForRoom({
|
||||||
rid,
|
rid,
|
|
@ -1,6 +1,6 @@
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
|
||||||
// unlike getMessages, sync isn't required for threads, because loadMissedMessages does it already
|
// unlike getMessages, sync isn't required for threads, because loadMissedMessages does it already
|
||||||
const getThreadMessages = (tmid, rid) => RocketChat.loadThreadMessages({ tmid, rid });
|
const getThreadMessages = (tmid: string, rid: string) => RocketChat.loadThreadMessages({ tmid, rid });
|
||||||
|
|
||||||
export default getThreadMessages;
|
export default getThreadMessages;
|
|
@ -1,5 +0,0 @@
|
||||||
import RocketChat from '../../../lib/rocketchat';
|
|
||||||
|
|
||||||
const readMessages = (rid, newLastOpen) => RocketChat.readMessages(rid, newLastOpen, true);
|
|
||||||
|
|
||||||
export default readMessages;
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import RocketChat from '../../../lib/rocketchat';
|
||||||
|
|
||||||
|
const readMessages = (rid: string, newLastOpen: Date): Promise<void> => RocketChat.readMessages(rid, newLastOpen, true);
|
||||||
|
|
||||||
|
export default readMessages;
|
|
@ -950,7 +950,7 @@ SPEC CHECKSUMS:
|
||||||
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
||||||
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
||||||
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
FBLazyVector: e686045572151edef46010a6f819ade377dfeb4b
|
||||||
FBReactNativeSpec: 686ac17e193dcf7d5df4d772b224504dd2f3ad81
|
FBReactNativeSpec: 110d69378fce79af38271c39894b59fec7890221
|
||||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||||
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
||||||
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
||||||
|
|
Loading…
Reference in New Issue