Merge branch 'develop' into update.enable-multiline-android-tablets
This commit is contained in:
commit
c369baf63f
|
@ -3,7 +3,7 @@ defaults: &defaults
|
|||
|
||||
macos: &macos
|
||||
macos:
|
||||
xcode: "12.5.0"
|
||||
xcode: "13.3.0"
|
||||
resource_class: large
|
||||
|
||||
bash-env: &bash-env
|
||||
|
|
|
@ -1,45 +1,8 @@
|
|||
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
|
||||
import { render } from '@testing-library/react-native';
|
||||
|
||||
jest.mock('rn-fetch-blob', () => ({
|
||||
fs: {
|
||||
dirs: {
|
||||
DocumentDir: '/data/com.rocket.chat/documents',
|
||||
DownloadDir: '/data/com.rocket.chat/downloads'
|
||||
},
|
||||
exists: jest.fn(() => null)
|
||||
},
|
||||
fetch: jest.fn(() => null),
|
||||
config: jest.fn(() => null)
|
||||
}));
|
||||
|
||||
jest.mock('react-native-file-viewer', () => ({
|
||||
open: jest.fn(() => null)
|
||||
}));
|
||||
|
||||
jest.mock('../app/lib/database', () => jest.fn(() => null));
|
||||
global.Date.now = jest.fn(() => new Date('2019-10-10').getTime());
|
||||
|
||||
jest.mock('react-native-mmkv-storage', () => {
|
||||
return {
|
||||
Loader: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
setProcessingMode: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
withEncryption: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
initialize: jest.fn()
|
||||
};
|
||||
})
|
||||
};
|
||||
})
|
||||
};
|
||||
}),
|
||||
create: jest.fn(),
|
||||
MODES: { MULTI_PROCESS: '' }
|
||||
};
|
||||
});
|
||||
|
||||
const converter = new Stories2SnapsConverter();
|
||||
|
||||
initStoryshots({
|
||||
|
|
|
@ -144,7 +144,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "4.26.0"
|
||||
versionName "4.26.2"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
if (!isFoss) {
|
||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Action } from 'redux';
|
||||
|
||||
import { MESSAGES } from './actionsTypes';
|
||||
|
||||
type IMessage = Record<string, string>;
|
||||
import { IMessage } from '../definitions';
|
||||
|
||||
interface IReplyBroadcast extends Action {
|
||||
message: IMessage;
|
||||
|
|
|
@ -19,7 +19,7 @@ const mentions = {
|
|||
mentionOtherColor: '#F3BE08'
|
||||
};
|
||||
|
||||
export const themes: any = {
|
||||
export const colors = {
|
||||
light: {
|
||||
backgroundColor: '#ffffff',
|
||||
focusedBackground: '#ffffff',
|
||||
|
@ -174,3 +174,5 @@ export const themes: any = {
|
|||
...mentions
|
||||
}
|
||||
};
|
||||
|
||||
export const themes: any = colors;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react';
|
|||
|
||||
import ActionSheet from './ActionSheet';
|
||||
|
||||
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void };
|
||||
export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void; danger?: boolean };
|
||||
|
||||
export type TActionSheetOptions = {
|
||||
options: TActionSheetOptionsItem[];
|
||||
|
|
|
@ -8,6 +8,7 @@ import { avatarURL } from '../../utils/avatar';
|
|||
import { SubscriptionType } from '../../definitions/ISubscription';
|
||||
import Emoji from '../markdown/Emoji';
|
||||
import { IAvatar } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const Avatar = React.memo(
|
||||
({
|
||||
|
@ -18,7 +19,6 @@ const Avatar = React.memo(
|
|||
user,
|
||||
onPress,
|
||||
emoji,
|
||||
theme,
|
||||
getCustomEmoji,
|
||||
avatarETag,
|
||||
isStatic,
|
||||
|
@ -34,6 +34,8 @@ const Avatar = React.memo(
|
|||
return null;
|
||||
}
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const avatarStyle = {
|
||||
width: size,
|
||||
height: size,
|
||||
|
@ -44,7 +46,7 @@ const Avatar = React.memo(
|
|||
if (emoji) {
|
||||
image = (
|
||||
<Emoji
|
||||
theme={theme!}
|
||||
theme={theme}
|
||||
baseUrl={server}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
isMessageContainsOnlyEmoji
|
||||
|
|
|
@ -5,13 +5,11 @@ import { Observable, Subscription } from 'rxjs';
|
|||
|
||||
import database from '../../lib/database';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import { TSubscriptionModel, TUserModel } from '../../definitions';
|
||||
import { IApplicationState, TSubscriptionModel, TUserModel } from '../../definitions';
|
||||
import Avatar from './Avatar';
|
||||
import { IAvatar } from './interfaces';
|
||||
|
||||
class AvatarContainer extends React.Component<IAvatar, any> {
|
||||
private mounted: boolean;
|
||||
|
||||
private subscription?: Subscription;
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -21,16 +19,11 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
|||
|
||||
constructor(props: IAvatar) {
|
||||
super(props);
|
||||
this.mounted = false;
|
||||
this.state = { avatarETag: '' };
|
||||
this.init();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
componentDidUpdate(prevProps: IAvatar) {
|
||||
const { text, type } = this.props;
|
||||
if (prevProps.text !== text || prevProps.type !== type) {
|
||||
this.init();
|
||||
|
@ -88,12 +81,7 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
|||
const observable = record.observe() as Observable<TSubscriptionModel | TUserModel>;
|
||||
this.subscription = observable.subscribe(r => {
|
||||
const { avatarETag } = r;
|
||||
if (this.mounted) {
|
||||
this.setState({ avatarETag });
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.state.avatarETag = avatarETag;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -105,12 +93,12 @@ class AvatarContainer extends React.Component<IAvatar, any> {
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
user: getUserSelector(state),
|
||||
server: state.share.server.server || state.server.server,
|
||||
serverVersion: state.share.server.version || state.server.version,
|
||||
blockUnauthenticatedAccess:
|
||||
state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess ??
|
||||
(state.share.settings?.Accounts_AvatarBlockUnauthenticatedAccess as boolean) ??
|
||||
state.settings.Accounts_AvatarBlockUnauthenticatedAccess ??
|
||||
true
|
||||
});
|
||||
|
|
|
@ -16,12 +16,11 @@ export interface IAvatar {
|
|||
id?: string;
|
||||
token?: string;
|
||||
};
|
||||
theme?: string;
|
||||
onPress?: () => void;
|
||||
getCustomEmoji?: TGetCustomEmoji;
|
||||
avatarETag?: string;
|
||||
isStatic?: boolean | string;
|
||||
rid?: string;
|
||||
blockUnauthenticatedAccess?: boolean;
|
||||
serverVersion: string;
|
||||
serverVersion: string | null;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ const CustomEmoji = React.memo(
|
|||
<FastImage
|
||||
style={style}
|
||||
source={{
|
||||
// @ts-ignore
|
||||
uri: `${baseUrl}/emoji-custom/${encodeURIComponent(emoji.content || emoji.name)}.${emoji.extension}`,
|
||||
priority: FastImage.priority.high
|
||||
}}
|
||||
|
|
|
@ -56,6 +56,7 @@ class EmojiCategory extends React.Component<Partial<IEmojiCategory>> {
|
|||
contentContainerStyle={{ marginHorizontal }}
|
||||
// rerender FlatList in case of width changes
|
||||
key={`emoji-category-${width}`}
|
||||
// @ts-ignore
|
||||
keyExtractor={item => (item && item.isCustom && item.content) || item}
|
||||
data={emojis}
|
||||
extraData={this.props}
|
||||
|
|
|
@ -109,6 +109,7 @@ class EmojiPicker extends Component<IEmojiPickerProps, IEmojiPickerState> {
|
|||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||
let freqEmojiRecord: any;
|
||||
try {
|
||||
// @ts-ignore
|
||||
freqEmojiRecord = await freqEmojiCollection.find(emoji.content);
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
|
|
|
@ -285,7 +285,7 @@ class LoginServices extends React.PureComponent<ILoginServicesProps, ILoginServi
|
|||
toValue: height,
|
||||
duration: 300,
|
||||
easing: Easing.inOut(Easing.quad),
|
||||
useNativeDriver: true
|
||||
useNativeDriver: false
|
||||
}).start();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||
|
@ -10,26 +10,30 @@ import database from '../../lib/database';
|
|||
import { Button } from '../ActionSheet';
|
||||
import { useDimensions } from '../../dimensions';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import { IEmoji } from '../../definitions/IEmoji';
|
||||
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
||||
import { TAnyMessageModel } from '../../definitions';
|
||||
import { IEmoji } from '../../definitions/IEmoji';
|
||||
|
||||
interface IHeader {
|
||||
handleReaction: Function;
|
||||
type TItem = TFrequentlyUsedEmojiModel | string;
|
||||
|
||||
export interface IHeader {
|
||||
handleReaction: (emoji: TItem, message: TAnyMessageModel) => void;
|
||||
server: string;
|
||||
message: object;
|
||||
message: TAnyMessageModel;
|
||||
isMasterDetail: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
type TOnReaction = ({ emoji }: { emoji: TItem }) => void;
|
||||
|
||||
interface THeaderItem {
|
||||
item: IEmoji;
|
||||
onReaction: Function;
|
||||
item: TItem;
|
||||
onReaction: TOnReaction;
|
||||
server: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface THeaderFooter {
|
||||
onReaction: any;
|
||||
onReaction: TOnReaction;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
|
@ -62,25 +66,32 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const keyExtractor = (item: any) => item?.id || item;
|
||||
const keyExtractor = (item: TItem) => {
|
||||
const emojiModel = item as TFrequentlyUsedEmojiModel;
|
||||
return (emojiModel.id ? emojiModel.content : item) as string;
|
||||
};
|
||||
|
||||
const DEFAULT_EMOJIS = ['clap', '+1', 'heart_eyes', 'grinning', 'thinking_face', 'smiley'];
|
||||
|
||||
const HeaderItem = React.memo(({ item, onReaction, server, theme }: THeaderItem) => (
|
||||
const HeaderItem = ({ item, onReaction, server, theme }: THeaderItem) => {
|
||||
const emojiModel = item as TFrequentlyUsedEmojiModel;
|
||||
const emoji = (emojiModel.id ? emojiModel.content : item) as string;
|
||||
return (
|
||||
<Button
|
||||
testID={`message-actions-emoji-${item.content || item}`}
|
||||
onPress={() => onReaction({ emoji: `:${item.content || item}:` })}
|
||||
testID={`message-actions-emoji-${emoji}`}
|
||||
onPress={() => onReaction({ emoji: `:${emoji}:` })}
|
||||
style={[styles.headerItem, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||
theme={theme}>
|
||||
{item?.isCustom ? (
|
||||
<CustomEmoji style={styles.customEmoji} emoji={item} baseUrl={server} />
|
||||
{emojiModel?.isCustom ? (
|
||||
<CustomEmoji style={styles.customEmoji} emoji={emojiModel as IEmoji} baseUrl={server} />
|
||||
) : (
|
||||
<Text style={styles.headerIcon}>{shortnameToUnicode(`:${item.content || item}:`)}</Text>
|
||||
<Text style={styles.headerIcon}>{shortnameToUnicode(`:${emoji}:`)}</Text>
|
||||
)}
|
||||
</Button>
|
||||
));
|
||||
);
|
||||
};
|
||||
|
||||
const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
||||
const HeaderFooter = ({ onReaction, theme }: THeaderFooter) => (
|
||||
<Button
|
||||
testID='add-reaction'
|
||||
onPress={onReaction}
|
||||
|
@ -88,17 +99,19 @@ const HeaderFooter = React.memo(({ onReaction, theme }: THeaderFooter) => (
|
|||
theme={theme}>
|
||||
<CustomIcon name='reaction-add' size={24} color={themes[theme].bodyText} />
|
||||
</Button>
|
||||
));
|
||||
);
|
||||
|
||||
const Header = React.memo(({ handleReaction, server, message, isMasterDetail, theme }: IHeader) => {
|
||||
const [items, setItems] = useState<(TFrequentlyUsedEmojiModel | string)[]>([]);
|
||||
const { width, height }: any = useDimensions();
|
||||
const Header = React.memo(({ handleReaction, server, message, isMasterDetail }: IHeader) => {
|
||||
const [items, setItems] = useState<TItem[]>([]);
|
||||
const { width, height } = useDimensions();
|
||||
const { theme } = useTheme();
|
||||
|
||||
// TODO: create custom hook to re-render based on screen size
|
||||
const setEmojis = async () => {
|
||||
try {
|
||||
const db = database.active;
|
||||
const freqEmojiCollection = db.get('frequently_used_emojis');
|
||||
let freqEmojis: (TFrequentlyUsedEmojiModel | string)[] = await freqEmojiCollection.query().fetch();
|
||||
let freqEmojis: TItem[] = await freqEmojiCollection.query().fetch();
|
||||
|
||||
const isLandscape = width > height;
|
||||
const size = (isLandscape || isMasterDetail ? width / 2 : width) - CONTAINER_MARGIN * 2;
|
||||
|
@ -115,22 +128,21 @@ const Header = React.memo(({ handleReaction, server, message, isMasterDetail, th
|
|||
setEmojis();
|
||||
}, []);
|
||||
|
||||
const onReaction = ({ emoji }: { emoji: IEmoji }) => handleReaction(emoji, message);
|
||||
const onReaction: TOnReaction = ({ emoji }) => handleReaction(emoji, message);
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ item }) => <HeaderItem item={item} onReaction={onReaction} server={server} theme={theme!} />,
|
||||
[]
|
||||
const renderItem = ({ item }: { item: TItem }) => (
|
||||
<HeaderItem item={item} onReaction={onReaction} server={server} theme={theme} />
|
||||
);
|
||||
|
||||
const renderFooter = useCallback(() => <HeaderFooter onReaction={onReaction} theme={theme!} />, []);
|
||||
const renderFooter = () => <HeaderFooter onReaction={onReaction} theme={theme} />;
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: themes[theme!].focusedBackground }]}>
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].focusedBackground }]}>
|
||||
<FlatList
|
||||
data={items}
|
||||
renderItem={renderItem}
|
||||
ListFooterComponent={renderFooter}
|
||||
style={{ backgroundColor: themes[theme!].focusedBackground }}
|
||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
||||
keyExtractor={keyExtractor}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
scrollEnabled={false}
|
||||
|
@ -140,4 +152,4 @@ const Header = React.memo(({ handleReaction, server, message, isMasterDetail, th
|
|||
);
|
||||
});
|
||||
|
||||
export default withTheme(Header);
|
||||
export default Header;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||||
import { Alert, Clipboard, Share } from 'react-native';
|
||||
import { Alert, Share } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import { connect } from 'react-redux';
|
||||
import moment from 'moment';
|
||||
|
||||
|
@ -12,33 +13,33 @@ import { getMessageTranslation } from '../message/utils';
|
|||
import { LISTENER } from '../Toast';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import { showConfirmationAlert } from '../../utils/info';
|
||||
import { useActionSheet } from '../ActionSheet';
|
||||
import Header, { HEADER_HEIGHT } from './Header';
|
||||
import { TActionSheetOptionsItem, useActionSheet } from '../ActionSheet';
|
||||
import Header, { HEADER_HEIGHT, IHeader } from './Header';
|
||||
import events from '../../utils/log/events';
|
||||
import { ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||
import { IApplicationState, ILoggedUser, TAnyMessageModel, TSubscriptionModel } from '../../definitions';
|
||||
|
||||
export interface IMessageActions {
|
||||
room: TSubscriptionModel;
|
||||
tmid?: string;
|
||||
user: Pick<ILoggedUser, 'id'>;
|
||||
editInit: Function;
|
||||
reactionInit: Function;
|
||||
onReactionPress: Function;
|
||||
replyInit: Function;
|
||||
editInit: (message: TAnyMessageModel) => void;
|
||||
reactionInit: (message: TAnyMessageModel) => void;
|
||||
onReactionPress: (shortname: string, messageId: string) => void;
|
||||
replyInit: (message: TAnyMessageModel, mention: boolean) => void;
|
||||
isMasterDetail: boolean;
|
||||
isReadOnly: boolean;
|
||||
Message_AllowDeleting: boolean;
|
||||
Message_AllowDeleting_BlockDeleteInMinutes: number;
|
||||
Message_AllowEditing: boolean;
|
||||
Message_AllowEditing_BlockEditInMinutes: number;
|
||||
Message_AllowPinning: boolean;
|
||||
Message_AllowStarring: boolean;
|
||||
Message_Read_Receipt_Store_Users: boolean;
|
||||
Message_AllowDeleting?: boolean;
|
||||
Message_AllowDeleting_BlockDeleteInMinutes?: number;
|
||||
Message_AllowEditing?: boolean;
|
||||
Message_AllowEditing_BlockEditInMinutes?: number;
|
||||
Message_AllowPinning?: boolean;
|
||||
Message_AllowStarring?: boolean;
|
||||
Message_Read_Receipt_Store_Users?: boolean;
|
||||
server: string;
|
||||
editMessagePermission: [];
|
||||
deleteMessagePermission: [];
|
||||
forceDeleteMessagePermission: [];
|
||||
pinMessagePermission: [];
|
||||
editMessagePermission?: string[];
|
||||
deleteMessagePermission?: string[];
|
||||
forceDeleteMessagePermission?: string[];
|
||||
pinMessagePermission?: string[];
|
||||
}
|
||||
|
||||
const MessageActions = React.memo(
|
||||
|
@ -68,9 +69,14 @@ const MessageActions = React.memo(
|
|||
pinMessagePermission
|
||||
}: IMessageActions,
|
||||
ref
|
||||
): any => {
|
||||
let permissions: any = {};
|
||||
const { showActionSheet, hideActionSheet }: any = useActionSheet();
|
||||
) => {
|
||||
let permissions = {
|
||||
hasEditPermission: false,
|
||||
hasDeletePermission: false,
|
||||
hasForceDeletePermission: false,
|
||||
hasPinPermission: false
|
||||
};
|
||||
const { showActionSheet, hideActionSheet } = useActionSheet();
|
||||
|
||||
const getPermissions = async () => {
|
||||
try {
|
||||
|
@ -87,9 +93,9 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const isOwn = (message: any) => message.u && message.u._id === user.id;
|
||||
const isOwn = (message: TAnyMessageModel) => message.u && message.u._id === user.id;
|
||||
|
||||
const allowEdit = (message: any) => {
|
||||
const allowEdit = (message: TAnyMessageModel) => {
|
||||
if (isReadOnly) {
|
||||
return false;
|
||||
}
|
||||
|
@ -104,7 +110,7 @@ const MessageActions = React.memo(
|
|||
if (message.ts != null) {
|
||||
msgTs = moment(message.ts);
|
||||
}
|
||||
let currentTsDiff: any;
|
||||
let currentTsDiff = 0;
|
||||
if (msgTs != null) {
|
||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||
}
|
||||
|
@ -113,7 +119,7 @@ const MessageActions = React.memo(
|
|||
return true;
|
||||
};
|
||||
|
||||
const allowDelete = (message: any) => {
|
||||
const allowDelete = (message: TAnyMessageModel) => {
|
||||
if (isReadOnly) {
|
||||
return false;
|
||||
}
|
||||
|
@ -135,7 +141,7 @@ const MessageActions = React.memo(
|
|||
if (message.ts != null) {
|
||||
msgTs = moment(message.ts);
|
||||
}
|
||||
let currentTsDiff: any;
|
||||
let currentTsDiff = 0;
|
||||
if (msgTs != null) {
|
||||
currentTsDiff = moment().diff(msgTs, 'minutes');
|
||||
}
|
||||
|
@ -144,19 +150,19 @@ const MessageActions = React.memo(
|
|||
return true;
|
||||
};
|
||||
|
||||
const getPermalink = (message: any) => RocketChat.getPermalinkMessage(message);
|
||||
const getPermalink = (message: TAnyMessageModel) => RocketChat.getPermalinkMessage(message);
|
||||
|
||||
const handleReply = (message: any) => {
|
||||
const handleReply = (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_REPLY);
|
||||
replyInit(message, true);
|
||||
};
|
||||
|
||||
const handleEdit = (message: any) => {
|
||||
const handleEdit = (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_EDIT);
|
||||
editInit(message);
|
||||
};
|
||||
|
||||
const handleCreateDiscussion = (message: any) => {
|
||||
const handleCreateDiscussion = (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_DISCUSSION);
|
||||
const params = { message, channel: room, showCloseModal: true };
|
||||
if (isMasterDetail) {
|
||||
|
@ -166,7 +172,7 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const handleUnread = async (message: any) => {
|
||||
const handleUnread = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_UNREAD);
|
||||
const { id: messageId, ts } = message;
|
||||
const { rid } = room;
|
||||
|
@ -178,7 +184,7 @@ const MessageActions = React.memo(
|
|||
const subRecord = await subCollection.find(rid);
|
||||
await db.write(async () => {
|
||||
try {
|
||||
await subRecord.update(sub => (sub.lastOpen = ts));
|
||||
await subRecord.update(sub => (sub.lastOpen = ts as Date)); // TODO: reevaluate IMessage
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
@ -191,42 +197,44 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const handlePermalink = async (message: any) => {
|
||||
const handlePermalink = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_PERMALINK);
|
||||
try {
|
||||
const permalink: any = await getPermalink(message);
|
||||
Clipboard.setString(permalink);
|
||||
const permalink = await getPermalink(message);
|
||||
Clipboard.setString(permalink ?? '');
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Permalink_copied_to_clipboard') });
|
||||
} catch {
|
||||
logEvent(events.ROOM_MSG_ACTION_PERMALINK_F);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopy = async (message: any) => {
|
||||
const handleCopy = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_COPY);
|
||||
await Clipboard.setString(message?.attachments?.[0]?.description || message.msg);
|
||||
await Clipboard.setString((message?.attachments?.[0]?.description || message.msg) ?? '');
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||
};
|
||||
|
||||
const handleShare = async (message: any) => {
|
||||
const handleShare = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_SHARE);
|
||||
try {
|
||||
const permalink: any = await getPermalink(message);
|
||||
const permalink = await getPermalink(message);
|
||||
if (permalink) {
|
||||
Share.share({ message: permalink });
|
||||
}
|
||||
} catch {
|
||||
logEvent(events.ROOM_MSG_ACTION_SHARE_F);
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuote = (message: any) => {
|
||||
const handleQuote = (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_QUOTE);
|
||||
replyInit(message, false);
|
||||
};
|
||||
|
||||
const handleStar = async (message: any) => {
|
||||
const handleStar = async (message: TAnyMessageModel) => {
|
||||
logEvent(message.starred ? events.ROOM_MSG_ACTION_UNSTAR : events.ROOM_MSG_ACTION_STAR);
|
||||
try {
|
||||
await RocketChat.toggleStarMessage(message.id, message.starred);
|
||||
await RocketChat.toggleStarMessage(message.id, message.starred as boolean); // TODO: reevaluate `message.starred` type on IMessage
|
||||
EventEmitter.emit(LISTENER, { message: message.starred ? I18n.t('Message_unstarred') : I18n.t('Message_starred') });
|
||||
} catch (e) {
|
||||
logEvent(events.ROOM_MSG_ACTION_STAR_F);
|
||||
|
@ -234,20 +242,21 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const handlePin = async (message: any) => {
|
||||
const handlePin = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_PIN);
|
||||
try {
|
||||
await RocketChat.togglePinMessage(message.id, message.pinned);
|
||||
await RocketChat.togglePinMessage(message.id, message.pinned as boolean); // TODO: reevaluate `message.pinned` type on IMessage
|
||||
} catch (e) {
|
||||
logEvent(events.ROOM_MSG_ACTION_PIN_F);
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReaction = (shortname: any, message: any) => {
|
||||
const handleReaction: IHeader['handleReaction'] = (shortname, message) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_REACTION);
|
||||
if (shortname) {
|
||||
onReactionPress(shortname, message.id);
|
||||
// TODO: evaluate unification with IEmoji
|
||||
onReactionPress(shortname as any, message.id);
|
||||
} else {
|
||||
reactionInit(message);
|
||||
}
|
||||
|
@ -255,7 +264,7 @@ const MessageActions = React.memo(
|
|||
hideActionSheet();
|
||||
};
|
||||
|
||||
const handleReadReceipt = (message: any) => {
|
||||
const handleReadReceipt = (message: TAnyMessageModel) => {
|
||||
if (isMasterDetail) {
|
||||
Navigation.navigate('ModalStackNavigator', { screen: 'ReadReceiptsView', params: { messageId: message.id } });
|
||||
} else {
|
||||
|
@ -290,7 +299,7 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const handleReport = async (message: any) => {
|
||||
const handleReport = async (message: TAnyMessageModel) => {
|
||||
logEvent(events.ROOM_MSG_ACTION_REPORT);
|
||||
try {
|
||||
await RocketChat.reportMessage(message.id);
|
||||
|
@ -301,14 +310,14 @@ const MessageActions = React.memo(
|
|||
}
|
||||
};
|
||||
|
||||
const handleDelete = (message: any) => {
|
||||
const handleDelete = (message: TAnyMessageModel) => {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t('You_will_not_be_able_to_recover_this_message'),
|
||||
confirmationText: I18n.t('Delete'),
|
||||
onPress: async () => {
|
||||
try {
|
||||
logEvent(events.ROOM_MSG_ACTION_DELETE);
|
||||
await RocketChat.deleteMessage(message.id, message.subscription.id);
|
||||
await RocketChat.deleteMessage(message.id, message.subscription ? message.subscription.id : '');
|
||||
} catch (e) {
|
||||
logEvent(events.ROOM_MSG_ACTION_DELETE_F);
|
||||
log(e);
|
||||
|
@ -318,7 +327,7 @@ const MessageActions = React.memo(
|
|||
};
|
||||
|
||||
const getOptions = (message: TAnyMessageModel) => {
|
||||
let options: any = [];
|
||||
let options: TActionSheetOptionsItem[] = [];
|
||||
|
||||
// Reply
|
||||
if (!isReadOnly) {
|
||||
|
@ -462,16 +471,15 @@ const MessageActions = React.memo(
|
|||
}
|
||||
)
|
||||
);
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
server: state.server.server,
|
||||
Message_AllowDeleting: state.settings.Message_AllowDeleting,
|
||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes,
|
||||
Message_AllowEditing: state.settings.Message_AllowEditing,
|
||||
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes,
|
||||
Message_AllowPinning: state.settings.Message_AllowPinning,
|
||||
Message_AllowStarring: state.settings.Message_AllowStarring,
|
||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users,
|
||||
Message_AllowDeleting: state.settings.Message_AllowDeleting as boolean,
|
||||
Message_AllowDeleting_BlockDeleteInMinutes: state.settings.Message_AllowDeleting_BlockDeleteInMinutes as number,
|
||||
Message_AllowEditing: state.settings.Message_AllowEditing as boolean,
|
||||
Message_AllowEditing_BlockEditInMinutes: state.settings.Message_AllowEditing_BlockEditInMinutes as number,
|
||||
Message_AllowPinning: state.settings.Message_AllowPinning as boolean,
|
||||
Message_AllowStarring: state.settings.Message_AllowStarring as boolean,
|
||||
Message_Read_Receipt_Store_Users: state.settings.Message_Read_Receipt_Store_Users as boolean,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
editMessagePermission: state.permissions['edit-message'],
|
||||
deleteMessagePermission: state.permissions['delete-message'],
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { TouchableOpacity } from 'react-native';
|
||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||
|
||||
import styles from '../styles';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import MessageboxContext from '../Context';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import { useTheme } from '../../../theme';
|
||||
import ActivityIndicator from '../../ActivityIndicator';
|
||||
import MessageboxContext from '../Context';
|
||||
import styles from '../styles';
|
||||
|
||||
interface IMessageBoxCommandsPreviewItem {
|
||||
item: {
|
||||
|
@ -14,13 +15,13 @@ interface IMessageBoxCommandsPreviewItem {
|
|||
id: string;
|
||||
value: string;
|
||||
};
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => {
|
||||
const Item = ({ item }: IMessageBoxCommandsPreviewItem) => {
|
||||
const context = useContext(MessageboxContext);
|
||||
const { onPressCommandPreview } = context;
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
|
@ -37,7 +38,7 @@ const Item = ({ item, theme }: IMessageBoxCommandsPreviewItem) => {
|
|||
{loading ? <ActivityIndicator /> : null}
|
||||
</FastImage>
|
||||
) : (
|
||||
<CustomIcon name='attach' size={36} color={themes[theme!].actionTintColor} />
|
||||
<CustomIcon name='attach' size={36} color={themes[theme].actionTintColor} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
import { dequal } from 'dequal';
|
||||
import React from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
import Item from './Item';
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { withTheme } from '../../../theme';
|
||||
import { IPreviewItem } from '../../../definitions';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../styles';
|
||||
import Item from './Item';
|
||||
|
||||
interface IMessageBoxCommandsPreview {
|
||||
commandPreview: IPreviewItem[];
|
||||
showCommandPreview: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const CommandsPreview = React.memo(
|
||||
({ theme, commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
|
||||
({ commandPreview, showCommandPreview }: IMessageBoxCommandsPreview) => {
|
||||
if (!showCommandPreview) {
|
||||
return null;
|
||||
}
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<FlatList
|
||||
testID='commandbox-container'
|
||||
style={[styles.mentionList, { backgroundColor: themes[theme!].messageboxBackground }]}
|
||||
style={[styles.mentionList, { backgroundColor: themes[theme].messageboxBackground }]}
|
||||
data={commandPreview}
|
||||
renderItem={({ item }) => <Item item={item} theme={theme} />}
|
||||
renderItem={({ item }) => <Item item={item} />}
|
||||
keyExtractor={(item: any) => item.id}
|
||||
keyboardShouldPersistTaps='always'
|
||||
horizontal
|
||||
|
@ -33,9 +33,6 @@ const CommandsPreview = React.memo(
|
|||
);
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
if (prevProps.theme !== nextProps.theme) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.showCommandPreview !== nextProps.showCommandPreview) {
|
||||
return false;
|
||||
}
|
||||
|
@ -46,4 +43,4 @@ const CommandsPreview = React.memo(
|
|||
}
|
||||
);
|
||||
|
||||
export default withTheme(CommandsPreview);
|
||||
export default CommandsPreview;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
|
||||
// @ts-ignore
|
||||
const MessageboxContext = React.createContext<any>();
|
||||
const MessageboxContext = React.createContext<any>(null);
|
||||
export default MessageboxContext;
|
||||
|
|
|
@ -7,6 +7,7 @@ import EmojiPicker from '../EmojiPicker';
|
|||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
import { IEmoji } from '../../definitions/IEmoji';
|
||||
|
||||
interface IMessageBoxEmojiKeyboard {
|
||||
theme: string;
|
||||
|
@ -21,7 +22,7 @@ export default class EmojiKeyboard extends React.PureComponent<IMessageBoxEmojiK
|
|||
this.baseUrl = state.share.server.server || state.server.server;
|
||||
}
|
||||
|
||||
onEmojiSelected = (emoji: any) => {
|
||||
onEmojiSelected = (emoji: IEmoji) => {
|
||||
KeyboardRegistry.onItemSelected('EmojiKeyboard', { emoji });
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import React from 'react';
|
|||
import { CancelEditingButton, ToggleEmojiButton } from './buttons';
|
||||
|
||||
interface IMessageBoxLeftButtons {
|
||||
theme: string;
|
||||
showEmojiKeyboard: boolean;
|
||||
openEmoji(): void;
|
||||
closeEmoji(): void;
|
||||
|
@ -11,13 +10,11 @@ interface IMessageBoxLeftButtons {
|
|||
editCancel(): void;
|
||||
}
|
||||
|
||||
const LeftButtons = React.memo(
|
||||
({ theme, showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
||||
const LeftButtons = React.memo(({ showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||
return <CancelEditingButton onPress={editCancel} />;
|
||||
}
|
||||
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} theme={theme} />;
|
||||
}
|
||||
);
|
||||
return <ToggleEmojiButton show={showEmojiKeyboard} open={openEmoji} close={closeEmoji} />;
|
||||
});
|
||||
|
||||
export default LeftButtons;
|
||||
|
|
|
@ -5,23 +5,20 @@ import { ActionsButton, CancelEditingButton } from './buttons';
|
|||
import styles from './styles';
|
||||
|
||||
interface IMessageBoxLeftButtons {
|
||||
theme: string;
|
||||
showMessageBoxActions(): void;
|
||||
editing: boolean;
|
||||
editCancel(): void;
|
||||
isActionsEnabled: boolean;
|
||||
}
|
||||
|
||||
const LeftButtons = React.memo(
|
||||
({ theme, showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
||||
const LeftButtons = React.memo(({ showMessageBoxActions, editing, editCancel, isActionsEnabled }: IMessageBoxLeftButtons) => {
|
||||
if (editing) {
|
||||
return <CancelEditingButton onPress={editCancel} theme={theme} />;
|
||||
return <CancelEditingButton onPress={editCancel} />;
|
||||
}
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
||||
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||
}
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export default LeftButtons;
|
||||
|
|
|
@ -4,16 +4,18 @@ import { Text, TouchableOpacity } from 'react-native';
|
|||
import styles from '../styles';
|
||||
import I18n from '../../../i18n';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IMessageBoxFixedMentionItem {
|
||||
item: {
|
||||
username: string;
|
||||
};
|
||||
onPress: Function;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem) => (
|
||||
const FixedMentionItem = ({ item, onPress }: IMessageBoxFixedMentionItem) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.mentionItem,
|
||||
|
@ -29,5 +31,6 @@ const FixedMentionItem = ({ item, onPress, theme }: IMessageBoxFixedMentionItem)
|
|||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default FixedMentionItem;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||
import styles from '../styles';
|
||||
import MessageboxContext from '../Context';
|
||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||
import { IEmoji } from '../../../definitions/IEmoji';
|
||||
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||
import MessageboxContext from '../Context';
|
||||
import styles from '../styles';
|
||||
|
||||
interface IMessageBoxMentionEmoji {
|
||||
item: IEmoji;
|
||||
|
@ -22,8 +21,4 @@ const MentionEmoji = ({ item }: IMessageBoxMentionEmoji) => {
|
|||
return <Text style={styles.mentionItemEmoji}>{shortnameToUnicode(`:${item}:`)}</Text>;
|
||||
};
|
||||
|
||||
MentionEmoji.propTypes = {
|
||||
item: PropTypes.object
|
||||
};
|
||||
|
||||
export default MentionEmoji;
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { View, Text, ActivityIndicator, TouchableOpacity } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ActivityIndicator, Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||
import styles from '../styles';
|
||||
import sharedStyles from '../../../views/Styles';
|
||||
import I18n from '../../../i18n';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import I18n from '../../../i18n';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import { useTheme } from '../../../theme';
|
||||
import sharedStyles from '../../../views/Styles';
|
||||
import { MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||
import MessageboxContext from '../Context';
|
||||
import styles from '../styles';
|
||||
|
||||
const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => {
|
||||
interface IMentionHeaderList {
|
||||
trackingType: string;
|
||||
hasMentions: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const MentionHeaderList = ({ trackingType, hasMentions, loading }: IMentionHeaderList) => {
|
||||
const { theme } = useTheme();
|
||||
const context = useContext(MessageboxContext);
|
||||
const { onPressNoMatchCanned } = context;
|
||||
|
||||
|
@ -39,11 +46,4 @@ const MentionHeaderList = ({ trackingType, hasMentions, theme, loading }) => {
|
|||
return null;
|
||||
};
|
||||
|
||||
MentionHeaderList.propTypes = {
|
||||
trackingType: PropTypes.string,
|
||||
hasMentions: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
loading: PropTypes.bool
|
||||
};
|
||||
|
||||
export default MentionHeaderList;
|
|
@ -1,14 +1,15 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Text, TouchableOpacity } from 'react-native';
|
||||
|
||||
import styles from '../styles';
|
||||
import Avatar from '../../Avatar';
|
||||
import MessageboxContext from '../Context';
|
||||
import FixedMentionItem from './FixedMentionItem';
|
||||
import MentionEmoji from './MentionEmoji';
|
||||
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { IEmoji } from '../../../definitions/IEmoji';
|
||||
import { useTheme } from '../../../theme';
|
||||
import Avatar from '../../Avatar';
|
||||
import { MENTIONS_TRACKING_TYPE_CANNED, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_EMOJIS } from '../constants';
|
||||
import MessageboxContext from '../Context';
|
||||
import styles from '../styles';
|
||||
import FixedMentionItem from './FixedMentionItem';
|
||||
import MentionEmoji from './MentionEmoji';
|
||||
|
||||
interface IMessageBoxMentionItem {
|
||||
item: {
|
||||
|
@ -21,11 +22,48 @@ interface IMessageBoxMentionItem {
|
|||
text: string;
|
||||
} & IEmoji;
|
||||
trackingType: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
||||
const MentionItemContent = React.memo(({ trackingType, item }: IMessageBoxMentionItem) => {
|
||||
const { theme } = useTheme();
|
||||
switch (trackingType) {
|
||||
case MENTIONS_TRACKING_TYPE_EMOJIS:
|
||||
return (
|
||||
<>
|
||||
<MentionEmoji item={item} />
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
|
||||
</>
|
||||
);
|
||||
case MENTIONS_TRACKING_TYPE_COMMANDS:
|
||||
return (
|
||||
<>
|
||||
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
|
||||
</>
|
||||
);
|
||||
case MENTIONS_TRACKING_TYPE_CANNED:
|
||||
return (
|
||||
<>
|
||||
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
|
||||
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
|
||||
{item.text}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const MentionItem = ({ item, trackingType }: IMessageBoxMentionItem) => {
|
||||
const context = useContext(MessageboxContext);
|
||||
const { theme } = useTheme();
|
||||
const { onPressMention } = context;
|
||||
|
||||
const defineTestID = (type: string) => {
|
||||
|
@ -44,43 +82,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
|||
const testID = defineTestID(trackingType);
|
||||
|
||||
if (item.username === 'all' || item.username === 'here') {
|
||||
return <FixedMentionItem item={item} onPress={onPressMention} theme={theme} />;
|
||||
}
|
||||
|
||||
let content = (
|
||||
<>
|
||||
<Avatar style={styles.avatar} text={item.username || item.name} size={30} type={item.t} />
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.username || item.name || item}</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
if (trackingType === MENTIONS_TRACKING_TYPE_EMOJIS) {
|
||||
content = (
|
||||
<>
|
||||
<MentionEmoji item={item} />
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>:{item.name || item}:</Text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) {
|
||||
content = (
|
||||
<>
|
||||
<Text style={[styles.slash, { backgroundColor: themes[theme].borderColor, color: themes[theme].tintColor }]}>/</Text>
|
||||
<Text style={[styles.mentionText, { color: themes[theme].titleText }]}>{item.id}</Text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (trackingType === MENTIONS_TRACKING_TYPE_CANNED) {
|
||||
content = (
|
||||
<>
|
||||
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
|
||||
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
|
||||
{item.text}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
return <FixedMentionItem item={item} onPress={onPressMention} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -94,7 +96,7 @@ const MentionItem = ({ item, trackingType, theme }: IMessageBoxMentionItem) => {
|
|||
]}
|
||||
onPress={() => onPressMention(item)}
|
||||
testID={testID}>
|
||||
{content}
|
||||
<MentionItemContent item={item} trackingType={trackingType} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,29 +6,30 @@ import MentionHeaderList from './MentionHeaderList';
|
|||
import styles from '../styles';
|
||||
import MentionItem from './MentionItem';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IMessageBoxMentions {
|
||||
mentions: any[];
|
||||
trackingType: string;
|
||||
theme: string;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const Mentions = React.memo(
|
||||
({ mentions, trackingType, theme, loading }: IMessageBoxMentions) => {
|
||||
({ mentions, trackingType, loading }: IMessageBoxMentions) => {
|
||||
if (!trackingType) {
|
||||
return null;
|
||||
}
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View testID='messagebox-container'>
|
||||
<FlatList
|
||||
style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||
ListHeaderComponent={() => (
|
||||
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} theme={theme} loading={loading} />
|
||||
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} loading={loading} />
|
||||
)}
|
||||
data={mentions}
|
||||
extraData={mentions}
|
||||
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} theme={theme} />}
|
||||
renderItem={({ item }) => <MentionItem item={item} trackingType={trackingType} />}
|
||||
keyExtractor={item => item.rid || item.name || item.command || item.shortcut || item}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
|
@ -39,9 +40,6 @@ const Mentions = React.memo(
|
|||
if (prevProps.loading !== nextProps.loading) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.theme !== nextProps.theme) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.trackingType !== nextProps.trackingType) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -47,23 +47,17 @@ const RECORDING_MODE = {
|
|||
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
|
||||
};
|
||||
|
||||
const formatTime = function (seconds: any) {
|
||||
let minutes: any = Math.floor(seconds / 60);
|
||||
seconds %= 60;
|
||||
if (minutes < 10) {
|
||||
minutes = `0${minutes}`;
|
||||
}
|
||||
if (seconds < 10) {
|
||||
seconds = `0${seconds}`;
|
||||
}
|
||||
return `${minutes}:${seconds}`;
|
||||
const formatTime = function (time: number) {
|
||||
const minutes = Math.floor(time / 60);
|
||||
const seconds = time % 60;
|
||||
const min = minutes < 10 ? `0${minutes}` : minutes;
|
||||
const sec = seconds < 10 ? `0${seconds}` : seconds;
|
||||
return `${min}:${sec}`;
|
||||
};
|
||||
|
||||
export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAudioProps, any> {
|
||||
private isRecorderBusy: boolean;
|
||||
|
||||
private recording: any;
|
||||
|
||||
private recording!: Audio.Recording;
|
||||
private LastDuration: number;
|
||||
|
||||
constructor(props: IMessageBoxRecordAudioProps) {
|
||||
|
@ -112,7 +106,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
|||
return false;
|
||||
};
|
||||
|
||||
onRecordingStatusUpdate = (status: any) => {
|
||||
onRecordingStatusUpdate = (status: Audio.RecordingStatus) => {
|
||||
this.setState({
|
||||
isRecording: status.isRecording,
|
||||
recordingDurationMillis: status.durationMillis
|
||||
|
@ -157,7 +151,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
|||
await this.recording.stopAndUnloadAsync();
|
||||
|
||||
const fileURI = this.recording.getURI();
|
||||
const fileData = await getInfoAsync(fileURI);
|
||||
const fileData = await getInfoAsync(fileURI as string);
|
||||
const fileInfo = {
|
||||
name: `${Date.now()}.m4a`,
|
||||
mime: 'audio/aac',
|
||||
|
|
|
@ -8,6 +8,8 @@ import { CustomIcon } from '../../lib/Icons';
|
|||
import sharedStyles from '../../views/Styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { IMessage } from '../../definitions/IMessage';
|
||||
import { useTheme } from '../../theme';
|
||||
import { IApplicationState } from '../../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -49,16 +51,15 @@ interface IMessageBoxReplyPreview {
|
|||
baseUrl: string;
|
||||
username: string;
|
||||
getCustomEmoji: Function;
|
||||
theme: string;
|
||||
useRealName: boolean;
|
||||
}
|
||||
|
||||
const ReplyPreview = React.memo(
|
||||
({ message, Message_TimeFormat, replying, close, theme, useRealName }: IMessageBoxReplyPreview) => {
|
||||
({ message, Message_TimeFormat, replying, close, useRealName }: IMessageBoxReplyPreview) => {
|
||||
if (!replying) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { theme } = useTheme();
|
||||
const time = moment(message.ts).format(Message_TimeFormat);
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}>
|
||||
|
@ -75,16 +76,14 @@ const ReplyPreview = React.memo(
|
|||
</View>
|
||||
);
|
||||
},
|
||||
(prevProps: any, nextProps: any) =>
|
||||
prevProps.replying === nextProps.replying &&
|
||||
prevProps.theme === nextProps.theme &&
|
||||
prevProps.message.id === nextProps.message.id
|
||||
(prevProps: IMessageBoxReplyPreview, nextProps: IMessageBoxReplyPreview) =>
|
||||
prevProps.replying === nextProps.replying && prevProps.message.id === nextProps.message.id
|
||||
);
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
const mapStateToProps = (state: IApplicationState) => ({
|
||||
Message_TimeFormat: state.settings.Message_TimeFormat as string,
|
||||
baseUrl: state.server.server,
|
||||
useRealName: state.settings.UI_Use_Real_Name
|
||||
useRealName: state.settings.UI_Use_Real_Name as boolean
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(ReplyPreview);
|
||||
|
|
|
@ -5,24 +5,20 @@ import { ActionsButton, SendButton } from './buttons';
|
|||
import styles from './styles';
|
||||
|
||||
interface IMessageBoxRightButtons {
|
||||
theme: string;
|
||||
showSend: boolean;
|
||||
submit(): void;
|
||||
showMessageBoxActions(): void;
|
||||
isActionsEnabled: boolean;
|
||||
}
|
||||
|
||||
const RightButtons = React.memo(
|
||||
({ theme, showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
||||
const RightButtons = React.memo(({ showSend, submit, showMessageBoxActions, isActionsEnabled }: IMessageBoxRightButtons) => {
|
||||
if (showSend) {
|
||||
return <SendButton onPress={submit} theme={theme} />;
|
||||
return <SendButton onPress={submit} />;
|
||||
}
|
||||
if (isActionsEnabled) {
|
||||
return <ActionsButton onPress={showMessageBoxActions} theme={theme} />;
|
||||
return <ActionsButton onPress={showMessageBoxActions} />;
|
||||
}
|
||||
|
||||
return <View style={styles.buttonsWhitespace} />;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export default RightButtons;
|
||||
|
|
|
@ -3,16 +3,15 @@ import React from 'react';
|
|||
import { SendButton } from './buttons';
|
||||
|
||||
interface IMessageBoxRightButtons {
|
||||
theme: string;
|
||||
showSend: boolean;
|
||||
submit(): void;
|
||||
}
|
||||
|
||||
const RightButtons = React.memo(({ theme, showSend, submit }: IMessageBoxRightButtons) => {
|
||||
const RightButtons = ({ showSend, submit }: IMessageBoxRightButtons) => {
|
||||
if (showSend) {
|
||||
return <SendButton theme={theme} onPress={submit} />;
|
||||
return <SendButton onPress={submit} />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
export default RightButtons;
|
||||
|
|
|
@ -3,12 +3,11 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface IActionsButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const ActionsButton = React.memo(({ theme, onPress }: IActionsButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' theme={theme} />
|
||||
));
|
||||
const ActionsButton = ({ onPress }: IActionsButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-actions' accessibilityLabel='Message_actions' icon='add' />
|
||||
);
|
||||
|
||||
export default ActionsButton;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react';
|
||||
import { BorderlessButton } from 'react-native-gesture-handler';
|
||||
import React from 'react';
|
||||
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import styles from '../styles';
|
||||
import I18n from '../../../i18n';
|
||||
import i18n from '../../../i18n';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { themes } from '../../../constants/colors';
|
||||
|
||||
interface IBaseButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
testID: string;
|
||||
accessibilityLabel: string;
|
||||
|
@ -15,16 +15,18 @@ interface IBaseButton {
|
|||
color: string;
|
||||
}
|
||||
|
||||
const BaseButton = React.memo(({ onPress, testID, accessibilityLabel, icon, theme, color }: Partial<IBaseButton>) => (
|
||||
const BaseButton = ({ accessibilityLabel, icon, color, ...props }: Partial<IBaseButton>) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<BorderlessButton
|
||||
onPress={onPress}
|
||||
{...props}
|
||||
style={styles.actionButton}
|
||||
testID={testID}
|
||||
// @ts-ignore
|
||||
accessibilityLabel={I18n.t(accessibilityLabel)}
|
||||
accessibilityLabel={i18n.t(accessibilityLabel)}
|
||||
accessibilityTraits='button'>
|
||||
<CustomIcon name={icon} size={24} color={color ?? themes[theme!].auxiliaryTintColor} />
|
||||
<CustomIcon name={icon} size={24} color={color || themes[theme].auxiliaryTintColor} />
|
||||
</BorderlessButton>
|
||||
));
|
||||
);
|
||||
};
|
||||
|
||||
export default BaseButton;
|
||||
|
|
|
@ -3,18 +3,11 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface ICancelEditingButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const CancelEditingButton = React.memo(({ theme, onPress }: ICancelEditingButton) => (
|
||||
<BaseButton
|
||||
onPress={onPress}
|
||||
testID='messagebox-cancel-editing'
|
||||
accessibilityLabel='Cancel_editing'
|
||||
icon='close'
|
||||
theme={theme}
|
||||
/>
|
||||
));
|
||||
const CancelEditingButton = ({ onPress }: ICancelEditingButton) => (
|
||||
<BaseButton onPress={onPress} testID='messagebox-cancel-editing' accessibilityLabel='Cancel_editing' icon='close' />
|
||||
);
|
||||
|
||||
export default CancelEditingButton;
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
import BaseButton from './BaseButton';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
import BaseButton from './BaseButton';
|
||||
|
||||
interface ISendButton {
|
||||
theme: string;
|
||||
onPress(): void;
|
||||
}
|
||||
|
||||
const SendButton = React.memo(({ theme, onPress }: ISendButton) => (
|
||||
const SendButton = ({ onPress }: ISendButton) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={onPress}
|
||||
testID='messagebox-send-message'
|
||||
accessibilityLabel='Send_message'
|
||||
icon='send-filled'
|
||||
theme={theme}
|
||||
color={themes[theme].tintColor}
|
||||
/>
|
||||
));
|
||||
);
|
||||
};
|
||||
|
||||
export default SendButton;
|
||||
|
|
|
@ -3,33 +3,18 @@ import React from 'react';
|
|||
import BaseButton from './BaseButton';
|
||||
|
||||
interface IToggleEmojiButton {
|
||||
theme: string;
|
||||
show: boolean;
|
||||
open(): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
const ToggleEmojiButton = React.memo(({ theme, show, open, close }: IToggleEmojiButton) => {
|
||||
const ToggleEmojiButton = ({ show, open, close }: IToggleEmojiButton) => {
|
||||
if (show) {
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={close}
|
||||
testID='messagebox-close-emoji'
|
||||
accessibilityLabel='Close_emoji_selector'
|
||||
icon='keyboard'
|
||||
theme={theme}
|
||||
/>
|
||||
<BaseButton onPress={close} testID='messagebox-close-emoji' accessibilityLabel='Close_emoji_selector' icon='keyboard' />
|
||||
);
|
||||
}
|
||||
return (
|
||||
<BaseButton
|
||||
onPress={open}
|
||||
testID='messagebox-open-emoji'
|
||||
accessibilityLabel='Open_emoji_selector'
|
||||
icon='emoji'
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return <BaseButton onPress={open} testID='messagebox-open-emoji' accessibilityLabel='Open_emoji_selector' icon='emoji' />;
|
||||
};
|
||||
|
||||
export default ToggleEmojiButton;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||
import { Alert, Keyboard, NativeModules, Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { KeyboardAccessoryView } from 'react-native-ui-lib/keyboard';
|
||||
import ImagePicker, { Image, ImageOrVideo } from 'react-native-image-crop-picker';
|
||||
import ImagePicker, { Image, ImageOrVideo, Options } from 'react-native-image-crop-picker';
|
||||
import { dequal } from 'dequal';
|
||||
import DocumentPicker from 'react-native-document-picker';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
|
@ -50,7 +50,8 @@ import { sanitizeLikeString } from '../../lib/database/utils';
|
|||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { IMessage } from '../../definitions/IMessage';
|
||||
import { forceJpgExtension } from './forceJpgExtension';
|
||||
import { IPreviewItem, IUser } from '../../definitions';
|
||||
import { IBaseScreen, IPreviewItem, IUser, TSubscriptionModel, TThreadModel } from '../../definitions';
|
||||
import { MasterDetailInsideStackParamList } from '../../stacks/MasterDetailStack/types';
|
||||
|
||||
if (isAndroid) {
|
||||
require('./EmojiKeyboard');
|
||||
|
@ -63,18 +64,18 @@ const imagePickerConfig = {
|
|||
forceJpg: true
|
||||
};
|
||||
|
||||
const libraryPickerConfig = {
|
||||
const libraryPickerConfig: Options = {
|
||||
multiple: true,
|
||||
compressVideoPreset: 'Passthrough',
|
||||
mediaType: 'any',
|
||||
forceJpg: true
|
||||
};
|
||||
|
||||
const videoPickerConfig = {
|
||||
const videoPickerConfig: Options = {
|
||||
mediaType: 'video'
|
||||
};
|
||||
|
||||
export interface IMessageBoxProps {
|
||||
export interface IMessageBoxProps extends IBaseScreen<MasterDetailInsideStackParamList, any> {
|
||||
rid: string;
|
||||
baseUrl: string;
|
||||
message: IMessage;
|
||||
|
@ -97,7 +98,6 @@ export interface IMessageBoxProps {
|
|||
theme: string;
|
||||
replyCancel(): void;
|
||||
showSend: boolean;
|
||||
navigation: any;
|
||||
children: JSX.Element;
|
||||
isMasterDetail: boolean;
|
||||
showActionSheet: Function;
|
||||
|
@ -118,7 +118,7 @@ interface IMessageBoxState {
|
|||
commandPreview: IPreviewItem[];
|
||||
showCommandPreview: boolean;
|
||||
command: {
|
||||
appId?: any;
|
||||
appId?: string;
|
||||
};
|
||||
tshow: boolean;
|
||||
mentionLoading: boolean;
|
||||
|
@ -132,17 +132,15 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
|
||||
private focused: boolean;
|
||||
|
||||
private options: any;
|
||||
private imagePickerConfig: Options;
|
||||
|
||||
private imagePickerConfig: any;
|
||||
private libraryPickerConfig: Options;
|
||||
|
||||
private libraryPickerConfig: any;
|
||||
private videoPickerConfig: Options;
|
||||
|
||||
private videoPickerConfig: any;
|
||||
private room!: TSubscriptionModel;
|
||||
|
||||
private room: any;
|
||||
|
||||
private thread: any;
|
||||
private thread!: TThreadModel;
|
||||
|
||||
private unsubscribeFocus: any;
|
||||
|
||||
|
@ -713,7 +711,8 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
chooseFromLibrary = async () => {
|
||||
logEvent(events.ROOM_BOX_ACTION_LIBRARY);
|
||||
try {
|
||||
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as ImageOrVideo[];
|
||||
// The type can be video or photo, however the lib understands that it is just one of them.
|
||||
let attachments = (await ImagePicker.openPicker(this.libraryPickerConfig)) as unknown as ImageOrVideo[];
|
||||
attachments = attachments.map(att => forceJpgExtension(att));
|
||||
this.openShareView(attachments);
|
||||
} catch (e) {
|
||||
|
@ -757,12 +756,12 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
openShareView = (attachments: any) => {
|
||||
const { message, replyCancel, replyWithMention } = this.props;
|
||||
// Start a thread with an attachment
|
||||
let { thread } = this;
|
||||
let value: TThreadModel | IMessage = this.thread;
|
||||
if (replyWithMention) {
|
||||
thread = message;
|
||||
value = message;
|
||||
replyCancel();
|
||||
}
|
||||
Navigation.navigate('ShareView', { room: this.room, thread, attachments });
|
||||
Navigation.navigate('ShareView', { room: this.room, value, attachments });
|
||||
};
|
||||
|
||||
createDiscussion = () => {
|
||||
|
@ -1058,7 +1057,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
const commandsPreviewAndMentions = !recording ? (
|
||||
<>
|
||||
<CommandsPreview commandPreview={commandPreview} showCommandPreview={showCommandPreview} />
|
||||
<Mentions mentions={mentions} trackingType={trackingType} theme={theme} loading={mentionLoading} />
|
||||
<Mentions mentions={mentions} trackingType={trackingType} loading={mentionLoading} />
|
||||
</>
|
||||
) : null;
|
||||
|
||||
|
@ -1069,7 +1068,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
username={user.username}
|
||||
replying={replying}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import I18n from '../i18n';
|
|||
import { CustomIcon } from '../lib/Icons';
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { themes } from '../constants/colors';
|
||||
import { withTheme } from '../theme';
|
||||
import { useTheme, withTheme } from '../theme';
|
||||
import { TGetCustomEmoji } from '../definitions/IEmoji';
|
||||
import { TMessageModel, ILoggedUser } from '../definitions';
|
||||
import SafeAreaView from './SafeAreaView';
|
||||
|
@ -61,38 +61,37 @@ const styles = StyleSheet.create({
|
|||
const standardEmojiStyle = { fontSize: 20 };
|
||||
const customEmojiStyle = { width: 20, height: 20 };
|
||||
|
||||
interface IItem {
|
||||
interface ISharedFields {
|
||||
user?: Pick<ILoggedUser, 'username'>;
|
||||
baseUrl: string;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
interface IItem extends ISharedFields {
|
||||
item: {
|
||||
usernames: any;
|
||||
usernames: string[];
|
||||
emoji: string;
|
||||
};
|
||||
user?: Pick<ILoggedUser, 'username'>;
|
||||
baseUrl?: string;
|
||||
getCustomEmoji?: TGetCustomEmoji;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
interface IModalContent {
|
||||
interface IModalContent extends ISharedFields {
|
||||
message?: TMessageModel;
|
||||
onClose: Function;
|
||||
onClose: () => void;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IReactionsModal {
|
||||
message?: any;
|
||||
user?: Pick<ILoggedUser, 'username'>;
|
||||
interface IReactionsModal extends ISharedFields {
|
||||
message?: TMessageModel;
|
||||
isVisible: boolean;
|
||||
onClose(): void;
|
||||
baseUrl: string;
|
||||
getCustomEmoji?: TGetCustomEmoji;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const Item = React.memo(({ item, user, baseUrl, getCustomEmoji, theme }: IItem) => {
|
||||
const Item = React.memo(({ item, user, baseUrl, getCustomEmoji }: IItem) => {
|
||||
const { theme } = useTheme();
|
||||
const count = item.usernames.length;
|
||||
let usernames = item.usernames
|
||||
.slice(0, 3)
|
||||
.map((username: any) => (username === user?.username ? I18n.t('you') : username))
|
||||
.map((username: string) => (username === user?.username ? I18n.t('you') : username))
|
||||
.join(', ');
|
||||
if (count > 3) {
|
||||
usernames = `${usernames} ${I18n.t('and_more')} ${count - 3}`;
|
||||
|
@ -106,15 +105,15 @@ const Item = React.memo(({ item, user, baseUrl, getCustomEmoji, theme }: IItem)
|
|||
content={item.emoji}
|
||||
standardEmojiStyle={standardEmojiStyle}
|
||||
customEmojiStyle={customEmojiStyle}
|
||||
baseUrl={baseUrl!}
|
||||
getCustomEmoji={getCustomEmoji!}
|
||||
baseUrl={baseUrl}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.peopleItemContainer}>
|
||||
<Text style={[styles.reactCount, { color: themes[theme!].buttonText }]}>
|
||||
<Text style={[styles.reactCount, { color: themes[theme].buttonText }]}>
|
||||
{count === 1 ? I18n.t('1_person_reacted') : I18n.t('N_people_reacted', { n: count })}
|
||||
</Text>
|
||||
<Text style={[styles.peopleReacted, { color: themes[theme!].buttonText }]}>{usernames}</Text>
|
||||
<Text style={[styles.peopleReacted, { color: themes[theme].buttonText }]}>{usernames}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
@ -143,7 +142,9 @@ const ModalContent = React.memo(({ message, onClose, ...props }: IModalContent)
|
|||
});
|
||||
|
||||
const ReactionsModal = React.memo(
|
||||
({ isVisible, onClose, theme, ...props }: IReactionsModal) => (
|
||||
({ isVisible, onClose, ...props }: IReactionsModal) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<Modal
|
||||
isVisible={isVisible}
|
||||
onBackdropPress={onClose}
|
||||
|
@ -153,8 +154,9 @@ const ReactionsModal = React.memo(
|
|||
swipeDirection={['up', 'left', 'right', 'down']}>
|
||||
<ModalContent onClose={onClose} theme={theme} {...props} />
|
||||
</Modal>
|
||||
),
|
||||
(prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible && prevProps.theme === nextProps.theme
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => prevProps.isVisible === nextProps.isVisible
|
||||
);
|
||||
|
||||
ReactionsModal.displayName = 'ReactionsModal';
|
||||
|
|
|
@ -6,8 +6,8 @@ import sharedStyles from '../../views/Styles';
|
|||
import { themes } from '../../constants/colors';
|
||||
import { MarkdownPreview } from '../markdown';
|
||||
import RoomTypeIcon from '../RoomTypeIcon';
|
||||
import { withTheme } from '../../theme';
|
||||
import { TUserStatus } from '../../definitions';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const HIT_SLOP = {
|
||||
top: 5,
|
||||
|
@ -44,9 +44,8 @@ const styles = StyleSheet.create({
|
|||
|
||||
type TRoomHeaderSubTitle = {
|
||||
usersTyping: [];
|
||||
theme: string;
|
||||
subtitle: string;
|
||||
renderFunc: any;
|
||||
renderFunc?: () => React.ReactElement;
|
||||
scale: number;
|
||||
};
|
||||
|
||||
|
@ -55,7 +54,6 @@ type TRoomHeaderHeaderTitle = {
|
|||
tmid: string;
|
||||
prid: string;
|
||||
scale: number;
|
||||
theme: string;
|
||||
testID: string;
|
||||
};
|
||||
|
||||
|
@ -77,7 +75,8 @@ interface IRoomHeader {
|
|||
testID: string;
|
||||
}
|
||||
|
||||
const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }: TRoomHeaderSubTitle) => {
|
||||
const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, scale }: TRoomHeaderSubTitle) => {
|
||||
const { theme } = useTheme();
|
||||
const fontSize = getSubTitleSize(scale);
|
||||
// typing
|
||||
if (usersTyping.length) {
|
||||
|
@ -108,7 +107,8 @@ const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }
|
|||
return null;
|
||||
});
|
||||
|
||||
const HeaderTitle = React.memo(({ title, tmid, prid, scale, theme, testID }: TRoomHeaderHeaderTitle) => {
|
||||
const HeaderTitle = React.memo(({ title, tmid, prid, scale, testID }: TRoomHeaderHeaderTitle) => {
|
||||
const { theme } = useTheme();
|
||||
const titleStyle = { fontSize: TITLE_SIZE * scale, color: themes[theme].headerTitleColor };
|
||||
if (!tmid && !prid) {
|
||||
return (
|
||||
|
@ -133,12 +133,12 @@ const Header = React.memo(
|
|||
prid,
|
||||
tmid,
|
||||
onPress,
|
||||
theme,
|
||||
isGroupChat,
|
||||
teamMain,
|
||||
testID,
|
||||
usersTyping = []
|
||||
}: IRoomHeader) => {
|
||||
const { theme } = useTheme();
|
||||
const portrait = height > width;
|
||||
let scale = 1;
|
||||
|
||||
|
@ -153,7 +153,7 @@ const Header = React.memo(
|
|||
renderFunc = () => (
|
||||
<View style={styles.titleContainer}>
|
||||
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
||||
<Text style={[styles.subtitle, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
|
||||
<Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||
{parentTitle}
|
||||
</Text>
|
||||
</View>
|
||||
|
@ -168,25 +168,18 @@ const Header = React.memo(
|
|||
accessibilityLabel={title}
|
||||
onPress={handleOnPress}
|
||||
style={styles.container}
|
||||
// @ts-ignore
|
||||
disabled={tmid}
|
||||
disabled={!!tmid}
|
||||
hitSlop={HIT_SLOP}>
|
||||
<View style={styles.titleContainer}>
|
||||
{tmid ? null : (
|
||||
<RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />
|
||||
)}
|
||||
<HeaderTitle title={title} tmid={tmid} prid={prid} scale={scale} theme={theme!} testID={testID} />
|
||||
<HeaderTitle title={title} tmid={tmid} prid={prid} scale={scale} testID={testID} />
|
||||
</View>
|
||||
<SubTitle
|
||||
usersTyping={tmid ? [] : usersTyping}
|
||||
subtitle={subtitle}
|
||||
theme={theme!}
|
||||
renderFunc={renderFunc}
|
||||
scale={scale}
|
||||
/>
|
||||
<SubTitle usersTyping={tmid ? [] : usersTyping} subtitle={subtitle} renderFunc={renderFunc} scale={scale} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default withTheme(Header);
|
||||
export default Header;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { StyleSheet, ViewProps } from 'react-native';
|
||||
import { SafeAreaView as SafeAreaContext } from 'react-native-safe-area-context';
|
||||
|
||||
import { themes } from '../constants/colors';
|
||||
import { withTheme } from '../theme';
|
||||
import { useTheme } from '../theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
view: {
|
||||
|
@ -11,22 +11,24 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
interface ISafeAreaView {
|
||||
testID?: string;
|
||||
theme?: string;
|
||||
type SupportedChildren = React.ReactElement | React.ReactElement[] | null;
|
||||
type TSafeAreaViewChildren = SupportedChildren | SupportedChildren[];
|
||||
|
||||
interface ISafeAreaView extends ViewProps {
|
||||
vertical?: boolean;
|
||||
style?: object;
|
||||
children: React.ReactNode;
|
||||
children: TSafeAreaViewChildren;
|
||||
}
|
||||
|
||||
const SafeAreaView = React.memo(({ style, children, testID, theme, vertical = true, ...props }: ISafeAreaView) => (
|
||||
const SafeAreaView = React.memo(({ style, children, vertical = true, ...props }: ISafeAreaView) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<SafeAreaContext
|
||||
style={[styles.view, { backgroundColor: themes[theme!].auxiliaryBackground }, style]}
|
||||
style={[styles.view, { backgroundColor: themes[theme].auxiliaryBackground }, style]}
|
||||
edges={vertical ? ['right', 'left'] : undefined}
|
||||
testID={testID}
|
||||
{...props}>
|
||||
{children}
|
||||
</SafeAreaContext>
|
||||
));
|
||||
);
|
||||
});
|
||||
|
||||
export default withTheme(SafeAreaView);
|
||||
export default SafeAreaView;
|
||||
|
|
|
@ -47,11 +47,11 @@ const styles = StyleSheet.create({
|
|||
interface ISearchBox extends TextInputProps {
|
||||
value?: string;
|
||||
hasCancel?: boolean;
|
||||
onCancelPress?: Function;
|
||||
onCancelPress?: () => void;
|
||||
inputRef?: React.Ref<RNTextInput>;
|
||||
}
|
||||
|
||||
const CancelButton = ({ onCancelPress }: { onCancelPress?: Function }) => {
|
||||
const CancelButton = ({ onCancelPress }: { onCancelPress?: () => void }) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<Touchable onPress={onCancelPress} style={styles.cancel}>
|
||||
|
@ -84,7 +84,7 @@ const SearchBox = ({ hasCancel, onCancelPress, inputRef, ...props }: ISearchBox)
|
|||
{...props}
|
||||
/>
|
||||
</View>
|
||||
{hasCancel ? <CancelButton onCancelPress={onCancelPress} /> : null}
|
||||
{hasCancel && onCancelPress ? <CancelButton onCancelPress={onCancelPress} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,8 +4,10 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
|||
import Button from '../Button';
|
||||
import I18n from '../../i18n';
|
||||
import { IActions } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
export const Actions = ({ blockId, appId, elements, parser, theme }: IActions) => {
|
||||
export const Actions = ({ blockId, appId, elements, parser }: IActions) => {
|
||||
const { theme } = useTheme();
|
||||
const [showMoreVisible, setShowMoreVisible] = useState(() => elements && elements.length > 5);
|
||||
const renderedElements = showMoreVisible ? elements?.slice(0, 5) : elements;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import DateTimePicker from '@react-native-community/datetimepicker';
|
||||
import DateTimePicker, { Event } from '@react-native-community/datetimepicker';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
import moment from 'moment';
|
||||
|
@ -11,6 +11,7 @@ import { themes } from '../../constants/colors';
|
|||
import sharedStyles from '../../views/Styles';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { isAndroid } from '../../utils/deviceInfo';
|
||||
import { useTheme } from '../../theme';
|
||||
import ActivityIndicator from '../ActivityIndicator';
|
||||
import { IDatePicker } from './interfaces';
|
||||
|
||||
|
@ -36,14 +37,17 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
export const DatePicker = ({ element, language, action, context, theme, loading, value, error }: IDatePicker) => {
|
||||
export const DatePicker = ({ element, language, action, context, loading, value, error }: IDatePicker) => {
|
||||
const { theme } = useTheme();
|
||||
const [show, onShow] = useState(false);
|
||||
const initial_date = element?.initial_date;
|
||||
const placeholder = element?.placeholder;
|
||||
|
||||
const [currentDate, onChangeDate] = useState(new Date(initial_date || value));
|
||||
|
||||
const onChange = ({ nativeEvent: { timestamp } }: any, date: any) => {
|
||||
// timestamp as number exists in Event
|
||||
// @ts-ignore
|
||||
const onChange = ({ nativeEvent: { timestamp } }: Event, date?: Date) => {
|
||||
const newDate = date || new Date(timestamp);
|
||||
onChangeDate(newDate);
|
||||
action({ value: moment(newDate).format('YYYY-MM-DD') });
|
||||
|
@ -52,7 +56,9 @@ export const DatePicker = ({ element, language, action, context, theme, loading,
|
|||
}
|
||||
};
|
||||
|
||||
let button = <Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} theme={theme} />;
|
||||
let button = placeholder ? (
|
||||
<Button title={textParser([placeholder])} onPress={() => onShow(!show)} loading={loading} theme={theme} />
|
||||
) : null;
|
||||
|
||||
if (context === BLOCK_CONTEXT.FORM) {
|
||||
button = (
|
||||
|
|
|
@ -6,7 +6,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
|||
import ImageContainer from '../message/Image';
|
||||
import Navigation from '../../lib/Navigation';
|
||||
import { IThumb, IImage, IElement } from './interfaces';
|
||||
import { TThemeMode } from '../../definitions/ITheme';
|
||||
import { IAttachment } from '../../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
image: {
|
||||
|
@ -27,23 +27,22 @@ export const Thumb = ({ element, size = 88 }: IThumb) => (
|
|||
<FastImage style={[{ width: size, height: size }, styles.image]} source={{ uri: element?.imageUrl }} />
|
||||
);
|
||||
|
||||
export const Media = ({ element, theme }: IImage) => {
|
||||
const showAttachment = (attachment: any) => Navigation.navigate('AttachmentView', { attachment });
|
||||
export const Media = ({ element }: IImage) => {
|
||||
const showAttachment = (attachment: IAttachment) => Navigation.navigate('AttachmentView', { attachment });
|
||||
const imageUrl = element?.imageUrl ?? '';
|
||||
// @ts-ignore
|
||||
// TODO: delete ts-ignore after refactor Markdown and ImageContainer
|
||||
return <ImageContainer file={{ image_url: imageUrl }} imageUrl={imageUrl} showAttachment={showAttachment} theme={theme} />;
|
||||
|
||||
return <ImageContainer file={{ image_url: imageUrl }} imageUrl={imageUrl} showAttachment={showAttachment} />;
|
||||
};
|
||||
|
||||
const genericImage = (theme: TThemeMode, element: IElement, context?: number) => {
|
||||
const genericImage = (element: IElement, context?: number) => {
|
||||
switch (context) {
|
||||
case BLOCK_CONTEXT.SECTION:
|
||||
return <Thumb element={element} />;
|
||||
case BLOCK_CONTEXT.CONTEXT:
|
||||
return <ThumbContext element={element} />;
|
||||
default:
|
||||
return <Media element={element} theme={theme} />;
|
||||
return <Media element={element} />;
|
||||
}
|
||||
};
|
||||
|
||||
export const Image = ({ element, context, theme }: IImage) => genericImage(theme, element, context);
|
||||
export const Image = ({ element, context }: IImage) => genericImage(element, context);
|
||||
|
|
|
@ -7,26 +7,23 @@ import { themes } from '../../../constants/colors';
|
|||
import { textParser } from '../utils';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import styles from './styles';
|
||||
import { IItemData } from '.';
|
||||
|
||||
interface IChip {
|
||||
item: {
|
||||
value: string;
|
||||
imageUrl: string;
|
||||
text: string;
|
||||
};
|
||||
onSelect: Function;
|
||||
item: IItemData;
|
||||
onSelect: (item: IItemData) => void;
|
||||
style?: object;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IChips {
|
||||
items: [];
|
||||
onSelect: Function;
|
||||
items: IItemData[];
|
||||
onSelect: (item: IItemData) => void;
|
||||
style?: object;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const keyExtractor = (item: any) => item.value.toString();
|
||||
const keyExtractor = (item: IItemData) => item.value.toString();
|
||||
|
||||
const Chip = ({ item, onSelect, style, theme }: IChip) => (
|
||||
<Touchable
|
||||
|
|
|
@ -9,10 +9,10 @@ import styles from './styles';
|
|||
|
||||
interface IInput {
|
||||
children?: JSX.Element;
|
||||
onPress: Function;
|
||||
onPress: () => void;
|
||||
theme: string;
|
||||
inputStyle?: object;
|
||||
disabled?: boolean | object;
|
||||
disabled?: boolean | null;
|
||||
placeholder?: string;
|
||||
loading?: boolean;
|
||||
innerInputStyle?: object;
|
||||
|
|
|
@ -8,34 +8,31 @@ import * as List from '../../List';
|
|||
import { textParser } from '../utils';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import styles from './styles';
|
||||
import { IItemData } from '.';
|
||||
|
||||
interface IItem {
|
||||
item: {
|
||||
value: { name: string };
|
||||
text: { text: string };
|
||||
imageUrl: string;
|
||||
};
|
||||
selected: any;
|
||||
item: IItemData;
|
||||
selected?: string;
|
||||
onSelect: Function;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IItems {
|
||||
items: [];
|
||||
selected: [];
|
||||
items: IItemData[];
|
||||
selected: string[];
|
||||
onSelect: Function;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const keyExtractor = (item: any) => item.value.toString();
|
||||
const keyExtractor = (item: IItemData) => item.value.toString();
|
||||
|
||||
// RectButton doesn't work on modal (Android)
|
||||
const Item = ({ item, selected, onSelect, theme }: IItem) => {
|
||||
const itemName = item.value.name || item.text.text.toLowerCase();
|
||||
const itemName = item.value || item.text.text.toLowerCase();
|
||||
return (
|
||||
<Touchable
|
||||
testID={`multi-select-item-${itemName}`}
|
||||
key={item}
|
||||
key={itemName}
|
||||
onPress={() => onSelect(item)}
|
||||
style={[styles.item, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<>
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Animated, Easing, KeyboardAvoidingView, Modal, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
|
||||
import {
|
||||
Animated,
|
||||
Easing,
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
TextStyle
|
||||
} from 'react-native';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
|
||||
import Button from '../../Button';
|
||||
|
@ -8,26 +18,31 @@ import { textParser } from '../utils';
|
|||
import { themes } from '../../../constants/colors';
|
||||
import I18n from '../../../i18n';
|
||||
import { isIOS } from '../../../utils/deviceInfo';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { BlockContext, IText } from '../interfaces';
|
||||
import Chips from './Chips';
|
||||
import Items from './Items';
|
||||
import Input from './Input';
|
||||
import styles from './styles';
|
||||
|
||||
export interface IItemData {
|
||||
value: string;
|
||||
text: { text: string };
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
interface IMultiSelect {
|
||||
options: any[];
|
||||
options?: IItemData[];
|
||||
onChange: Function;
|
||||
placeholder: {
|
||||
text: string;
|
||||
};
|
||||
context?: number;
|
||||
placeholder?: IText;
|
||||
context?: BlockContext;
|
||||
loading?: boolean;
|
||||
multiselect?: boolean;
|
||||
onSearch?: () => void;
|
||||
onClose?: () => void;
|
||||
inputStyle?: object;
|
||||
inputStyle?: TextStyle;
|
||||
value?: any[];
|
||||
disabled?: boolean | object;
|
||||
theme: string;
|
||||
disabled?: boolean | null;
|
||||
innerInputStyle?: object;
|
||||
}
|
||||
|
||||
|
@ -54,9 +69,9 @@ export const MultiSelect = React.memo(
|
|||
onClose = () => {},
|
||||
disabled,
|
||||
inputStyle,
|
||||
theme,
|
||||
innerInputStyle
|
||||
}: IMultiSelect) => {
|
||||
const { theme } = useTheme();
|
||||
const [selected, select] = useState<any>(Array.isArray(values) ? values : []);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, onSearchChange] = useState('');
|
||||
|
@ -95,7 +110,7 @@ export const MultiSelect = React.memo(
|
|||
}).start(() => setShowContent(false));
|
||||
};
|
||||
|
||||
const onSelect = (item: any) => {
|
||||
const onSelect = (item: IItemData) => {
|
||||
const {
|
||||
value,
|
||||
text: { text }
|
||||
|
|
|
@ -6,6 +6,7 @@ import Touchable from 'react-native-platform-touchable';
|
|||
import { CustomIcon } from '../../lib/Icons';
|
||||
import ActivityIndicator from '../ActivityIndicator';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { useTheme } from '../../theme';
|
||||
import { BUTTON_HIT_SLOP } from '../message/utils';
|
||||
import * as List from '../List';
|
||||
import { IOption, IOptions, IOverflow } from './interfaces';
|
||||
|
@ -43,9 +44,10 @@ const Options = ({ options, onOptionPress, parser, theme }: IOptions) => (
|
|||
/>
|
||||
);
|
||||
|
||||
const touchable: { [key: string]: any } = {};
|
||||
const touchable: { [key: string]: Touchable | null } = {};
|
||||
|
||||
export const Overflow = ({ element, loading, action, parser, theme }: IOverflow) => {
|
||||
export const Overflow = ({ element, loading, action, parser }: IOverflow) => {
|
||||
const { theme } = useTheme();
|
||||
const options = element?.options || [];
|
||||
const blockId = element?.blockId || '';
|
||||
const [show, onShow] = useState(false);
|
||||
|
@ -58,7 +60,7 @@ export const Overflow = ({ element, loading, action, parser, theme }: IOverflow)
|
|||
return (
|
||||
<>
|
||||
<Touchable
|
||||
ref={(ref: any) => (touchable[blockId] = ref)}
|
||||
ref={ref => (touchable[blockId] = ref)}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
onPress={() => onShow(!show)}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
|
@ -71,6 +73,7 @@ export const Overflow = ({ element, loading, action, parser, theme }: IOverflow)
|
|||
</Touchable>
|
||||
<Popover
|
||||
isVisible={show}
|
||||
// fromView exists in Popover Component
|
||||
/* @ts-ignore*/
|
||||
fromView={touchable[blockId]}
|
||||
onRequestClose={() => onShow(false)}>
|
||||
|
|
|
@ -4,6 +4,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
|||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { IAccessoryComponent, IFields, ISection } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
|
@ -37,10 +38,14 @@ const Fields = ({ fields, parser, theme }: IFields) => (
|
|||
|
||||
const accessoriesRight = ['image', 'overflow'];
|
||||
|
||||
export const Section = ({ blockId, appId, text, fields, accessory, parser, theme }: ISection) => (
|
||||
export const Section = ({ blockId, appId, text, fields, accessory, parser }: ISection) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={[styles.content, accessory && accessoriesRight.includes(accessory.type) ? styles.row : styles.column]}>
|
||||
{text ? <View style={styles.text}>{parser.text(text)}</View> : null}
|
||||
{fields ? <Fields fields={fields} theme={theme} parser={parser} /> : null}
|
||||
{accessory ? <Accessory element={{ blockId, appId, ...accessory }} parser={parser} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,8 @@ import { CustomIcon } from '../../lib/Icons';
|
|||
import { textParser } from './utils';
|
||||
import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
||||
import ActivityIndicator from '../ActivityIndicator';
|
||||
import { useTheme } from '../../theme';
|
||||
import { IText, Option } from './interfaces';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
iosPadding: {
|
||||
|
@ -34,19 +36,16 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
interface ISelect {
|
||||
options: {
|
||||
text: string;
|
||||
value: string;
|
||||
}[];
|
||||
placeholder: string;
|
||||
options?: Option[];
|
||||
placeholder?: IText;
|
||||
onChange: Function;
|
||||
loading: boolean;
|
||||
disabled: boolean;
|
||||
disabled?: boolean;
|
||||
value: [];
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export const Select = ({ options = [], placeholder, onChange, loading, disabled, value: initialValue, theme }: ISelect) => {
|
||||
export const Select = ({ options = [], placeholder, onChange, loading, disabled, value: initialValue }: ISelect) => {
|
||||
const { theme } = useTheme();
|
||||
const [selected, setSelected] = useState(!Array.isArray(initialValue) && initialValue);
|
||||
const items = options.map(option => ({ label: textParser([option.text]), value: option.value }));
|
||||
const pickerStyle = {
|
||||
|
@ -80,6 +79,7 @@ export const Select = ({ options = [], placeholder, onChange, loading, disabled,
|
|||
}}
|
||||
Icon={Icon}
|
||||
textInputProps={{
|
||||
// style property was Omitted in lib, but can be used normally
|
||||
// @ts-ignore
|
||||
style: { ...styles.pickerText, color: selected ? themes[theme].titleText : themes[theme].auxiliaryText }
|
||||
}}
|
||||
|
|
|
@ -20,7 +20,7 @@ import { Input } from './Input';
|
|||
import { DatePicker } from './DatePicker';
|
||||
import { Overflow } from './Overflow';
|
||||
import { ThemeContext } from '../../theme';
|
||||
import { BlockContext, IButton, IInputIndex, IParser, IText } from './interfaces';
|
||||
import { BlockContext, IActions, IButton, IElement, IInputIndex, IParser, ISection, IText } from './interfaces';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
input: {
|
||||
|
@ -78,35 +78,28 @@ class MessageParser extends UiKitParserMessage {
|
|||
}
|
||||
|
||||
divider() {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
// @ts-ignore
|
||||
return <Divider theme={theme} />;
|
||||
return <Divider />;
|
||||
}
|
||||
|
||||
section(args: any) {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return <Section {...args} theme={theme} parser={this} />;
|
||||
section(args: ISection) {
|
||||
return <Section {...args} parser={this.current} />;
|
||||
}
|
||||
|
||||
actions(args: any) {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return <Actions {...args} theme={theme} parser={this} />;
|
||||
actions(args: IActions) {
|
||||
return <Actions {...args} parser={this.current} />;
|
||||
}
|
||||
|
||||
overflow(element: any, context: any) {
|
||||
const [{ loading }, action]: any = useBlockContext(element, context);
|
||||
const { theme }: any = useContext(ThemeContext);
|
||||
return <Overflow element={element} context={context} loading={loading} action={action} theme={theme} parser={this.current} />;
|
||||
overflow(element: IElement, context: BlockContext) {
|
||||
const [{ loading }, action] = useBlockContext(element, context);
|
||||
return <Overflow element={element} context={context} loading={loading} action={action} parser={this.current} />;
|
||||
}
|
||||
|
||||
datePicker(element: any, context: any) {
|
||||
const [{ loading, value, error, language }, action]: any = useBlockContext(element, context);
|
||||
const { theme }: any = useContext(ThemeContext);
|
||||
datePicker(element: IElement, context: BlockContext) {
|
||||
const [{ loading, value, error, language }, action] = useBlockContext(element, context);
|
||||
return (
|
||||
<DatePicker
|
||||
element={element}
|
||||
language={language}
|
||||
theme={theme}
|
||||
value={value}
|
||||
action={action}
|
||||
context={context}
|
||||
|
@ -116,9 +109,8 @@ class MessageParser extends UiKitParserMessage {
|
|||
);
|
||||
}
|
||||
|
||||
image(element: any, context: any) {
|
||||
const { theme }: any = useContext(ThemeContext);
|
||||
return <Image element={element} theme={theme} context={context} />;
|
||||
image(element: IElement, context: BlockContext) {
|
||||
return <Image element={element} context={context} />;
|
||||
}
|
||||
|
||||
context(args: any) {
|
||||
|
@ -126,24 +118,19 @@ class MessageParser extends UiKitParserMessage {
|
|||
return <Context {...args} theme={theme} parser={this} />;
|
||||
}
|
||||
|
||||
multiStaticSelect(element: any, context: any) {
|
||||
const [{ loading, value }, action]: any = useBlockContext(element, context);
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<MultiSelect {...element} theme={theme} value={value} onChange={action} context={context} loading={loading} multiselect />
|
||||
);
|
||||
multiStaticSelect(element: IElement, context: BlockContext) {
|
||||
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||
return <MultiSelect {...element} value={value} onChange={action} context={context} loading={loading} multiselect />;
|
||||
}
|
||||
|
||||
staticSelect(element: any, context: any) {
|
||||
const [{ loading, value }, action]: any = useBlockContext(element, context);
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return <Select {...element} theme={theme} value={value} onChange={action} loading={loading} />;
|
||||
staticSelect(element: IElement, context: BlockContext) {
|
||||
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||
return <Select {...element} value={value} onChange={action} loading={loading} />;
|
||||
}
|
||||
|
||||
selectInput(element: any, context: any) {
|
||||
const [{ loading, value }, action]: any = useBlockContext(element, context);
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return <MultiSelect {...element} theme={theme} value={value} onChange={action} context={context} loading={loading} />;
|
||||
selectInput(element: IElement, context: BlockContext) {
|
||||
const [{ loading, value }, action] = useBlockContext(element, context);
|
||||
return <MultiSelect {...element} value={value} onChange={action} context={context} loading={loading} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,8 +147,8 @@ class ModalParser extends UiKitParserModal {
|
|||
}
|
||||
|
||||
input({ element, blockId, appId, label, description, hint }: IInputIndex, context: number) {
|
||||
const [{ error }]: any = useBlockContext({ ...element, appId, blockId }, context);
|
||||
const { theme }: any = useContext(ThemeContext);
|
||||
const [{ error }] = useBlockContext({ ...element, appId, blockId }, context);
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<Input
|
||||
parser={this.current}
|
||||
|
@ -175,17 +162,15 @@ class ModalParser extends UiKitParserModal {
|
|||
);
|
||||
}
|
||||
|
||||
image(element: any, context: any) {
|
||||
const { theme }: any = useContext(ThemeContext);
|
||||
return <Image element={element} theme={theme} context={context} />;
|
||||
image(element: IElement, context: BlockContext) {
|
||||
return <Image element={element} context={context} />;
|
||||
}
|
||||
|
||||
plainInput(element: any, context: any) {
|
||||
const [{ loading, value, error }, action]: any = useBlockContext(element, context);
|
||||
plainInput(element: IElement, context: BlockContext) {
|
||||
const [{ loading, value, error }, action] = useBlockContext(element, context);
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const { multiline, actionId, placeholder } = element;
|
||||
return (
|
||||
// @ts-ignore
|
||||
<TextInput
|
||||
key={actionId}
|
||||
placeholder={plainText(placeholder)}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { TThemeMode } from '../../definitions/ITheme';
|
||||
|
||||
export enum ElementTypes {
|
||||
IMAGE = 'image',
|
||||
BUTTON = 'button',
|
||||
|
@ -87,10 +85,11 @@ export interface IElement {
|
|||
imageUrl?: string;
|
||||
appId?: string;
|
||||
blockId?: string;
|
||||
multiline?: boolean;
|
||||
}
|
||||
|
||||
export interface IText {
|
||||
type: ElementTypes;
|
||||
type?: ElementTypes;
|
||||
text: string;
|
||||
emoji?: boolean;
|
||||
}
|
||||
|
@ -98,12 +97,15 @@ export interface IText {
|
|||
export interface Option {
|
||||
text: IText;
|
||||
value: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
export interface IButton {
|
||||
type: ElementTypes;
|
||||
text: IText;
|
||||
actionId: string;
|
||||
blockId: string;
|
||||
appId: string;
|
||||
value?: any;
|
||||
style?: any;
|
||||
}
|
||||
|
@ -177,7 +179,6 @@ export interface IParser {
|
|||
}
|
||||
export interface IActions extends Block {
|
||||
parser?: IParser;
|
||||
theme: TThemeMode;
|
||||
}
|
||||
|
||||
export interface IContext extends Block {
|
||||
|
@ -191,7 +192,6 @@ export interface IDatePicker extends Partial<Block> {
|
|||
loading: boolean;
|
||||
value: string;
|
||||
error: string;
|
||||
theme: TThemeMode;
|
||||
}
|
||||
|
||||
export interface IInput extends Partial<Block> {
|
||||
|
@ -199,7 +199,7 @@ export interface IInput extends Partial<Block> {
|
|||
description: string;
|
||||
error: string;
|
||||
hint: string;
|
||||
theme: TThemeMode;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export interface IInputIndex {
|
||||
|
@ -217,8 +217,7 @@ export interface IThumb {
|
|||
}
|
||||
export interface IImage {
|
||||
element: IElement;
|
||||
theme: TThemeMode;
|
||||
context?: number;
|
||||
context?: BlockContext;
|
||||
}
|
||||
|
||||
// UiKit/Overflow
|
||||
|
@ -226,14 +225,13 @@ export interface IOverflow extends Partial<Block> {
|
|||
action: Function;
|
||||
loading: boolean;
|
||||
parser: IParser;
|
||||
theme: TThemeMode;
|
||||
context: number;
|
||||
}
|
||||
|
||||
interface PropsOption {
|
||||
onOptionPress: Function;
|
||||
parser: IParser;
|
||||
theme: TThemeMode;
|
||||
theme: string;
|
||||
}
|
||||
export interface IOptions extends PropsOption {
|
||||
options: Option[];
|
||||
|
@ -262,12 +260,11 @@ export interface ISection {
|
|||
text?: IText;
|
||||
accessory?: IAccessory;
|
||||
parser: IParser;
|
||||
theme: TThemeMode;
|
||||
fields?: any[];
|
||||
}
|
||||
|
||||
export interface IFields {
|
||||
parser: IParser;
|
||||
theme: TThemeMode;
|
||||
theme: string;
|
||||
fields: any[];
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
||||
|
||||
import { BlockContext } from './interfaces';
|
||||
import { BlockContext, IText } from './interfaces';
|
||||
|
||||
export const textParser = ([{ text }]: any) => text;
|
||||
export const textParser = ([{ text }]: IText[]) => text;
|
||||
|
||||
export const defaultContext: any = {
|
||||
action: (...args: any) => console.log(args),
|
||||
|
@ -27,7 +27,14 @@ type TFunctionReturn = (value: any) => Promise<void>;
|
|||
|
||||
type TReturn = [TObjectReturn, TFunctionReturn];
|
||||
|
||||
export const useBlockContext = ({ blockId, actionId, appId, initialValue }: any, context: BlockContext): TReturn => {
|
||||
interface IUseBlockContext {
|
||||
blockId?: string;
|
||||
actionId: string;
|
||||
appId?: string;
|
||||
initialValue?: string;
|
||||
}
|
||||
|
||||
export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUseBlockContext, context: BlockContext): TReturn => {
|
||||
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
|
||||
const { value = initialValue } = values[actionId] || {};
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
|
@ -18,7 +18,7 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
|||
|
||||
const handlePress = () => {
|
||||
const index = channels?.findIndex(channel => channel.name === hashtag);
|
||||
if (index && navToRoomInfo) {
|
||||
if (typeof index !== 'undefined' && navToRoomInfo) {
|
||||
const navParam = {
|
||||
t: 'c',
|
||||
rid: channels?.[index]._id
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Clipboard, Text } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export interface IUserMention {
|
||||
_id: string;
|
||||
username: string;
|
||||
username?: string;
|
||||
name?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Text, Clipboard } from 'react-native';
|
||||
import { Text } from 'react-native';
|
||||
import { Link as LinkProps } from '@rocket.chat/message-parser';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
import styles from '../styles';
|
||||
import I18n from '../../../i18n';
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
|||
import { dequal } from 'dequal';
|
||||
import { Text } from 'react-native';
|
||||
|
||||
import { IMessageAttachments, IMessageAttachedActions } from './interfaces';
|
||||
import { IMessageAttachments } from './interfaces';
|
||||
import Image from './Image';
|
||||
import Audio from './Audio';
|
||||
import Video from './Video';
|
||||
|
@ -13,30 +13,49 @@ import MessageContext from './Context';
|
|||
import { useTheme } from '../../theme';
|
||||
import { IAttachment } from '../../definitions';
|
||||
import CollapsibleQuote from './Components/CollapsibleQuote';
|
||||
import openLink from '../../utils/openLink';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
||||
const AttachedActions = ({ attachment }: IMessageAttachedActions) => {
|
||||
export type TElement = {
|
||||
type: string;
|
||||
msg?: string;
|
||||
url?: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
const AttachedActions = ({ attachment }: { attachment: IAttachment }) => {
|
||||
if (!attachment.actions) {
|
||||
return null;
|
||||
}
|
||||
const { onAnswerButtonPress } = useContext(MessageContext);
|
||||
const { theme } = useTheme();
|
||||
|
||||
const attachedButtons = attachment.actions.map((element: { type: string; msg: string; text: string }) => {
|
||||
if (element.type === 'button') {
|
||||
return <Button theme={theme} onPress={() => onAnswerButtonPress(element.msg)} title={element.text} />;
|
||||
const attachedButtons = attachment.actions.map((element: TElement) => {
|
||||
const onPress = () => {
|
||||
if (element.msg) {
|
||||
onAnswerButtonPress(element.msg);
|
||||
}
|
||||
|
||||
if (element.url) {
|
||||
openLink(element.url);
|
||||
}
|
||||
};
|
||||
|
||||
if (element.type === 'button') {
|
||||
return <Button theme={theme} onPress={onPress} title={element.text} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.text}>{attachment.text}</Text>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{attachment.text}</Text>
|
||||
{attachedButtons}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Attachments = React.memo(
|
||||
// @ts-ignore
|
||||
const Attachments: React.FC<IMessageAttachments> = React.memo(
|
||||
({ attachments, timeFormat, showAttachment, style, getCustomEmoji, isReply }: IMessageAttachments) => {
|
||||
if (!attachments || attachments.length === 0) {
|
||||
return null;
|
||||
|
@ -44,7 +63,7 @@ const Attachments = React.memo(
|
|||
|
||||
const { theme } = useTheme();
|
||||
|
||||
return attachments.map((file: IAttachment, index: number) => {
|
||||
const attachmentsElements = attachments.map((file: IAttachment, index: number) => {
|
||||
if (file && file.image_url) {
|
||||
return (
|
||||
<Image
|
||||
|
@ -54,7 +73,6 @@ const Attachments = React.memo(
|
|||
getCustomEmoji={getCustomEmoji}
|
||||
style={style}
|
||||
isReply={isReply}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -74,7 +92,6 @@ const Attachments = React.memo(
|
|||
getCustomEmoji={getCustomEmoji}
|
||||
style={style}
|
||||
isReply={isReply}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -90,6 +107,7 @@ const Attachments = React.memo(
|
|||
|
||||
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />;
|
||||
});
|
||||
return <>{attachmentsElements}</>;
|
||||
},
|
||||
(prevProps, nextProps) => dequal(prevProps.attachments, nextProps.attachments)
|
||||
);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
|
||||
import { Audio } from 'expo-av';
|
||||
import { Audio, AVPlaybackStatus } from 'expo-av';
|
||||
import Slider from '@react-native-community/slider';
|
||||
import moment from 'moment';
|
||||
import { dequal } from 'dequal';
|
||||
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
|
||||
import { Sound } from 'expo-av/build/Audio/Sound';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
import Markdown from '../markdown';
|
||||
|
@ -23,7 +24,7 @@ interface IButton {
|
|||
paused: boolean;
|
||||
theme: string;
|
||||
disabled?: boolean;
|
||||
onPress: Function;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
interface IMessageAudioProps {
|
||||
|
@ -108,7 +109,7 @@ Button.displayName = 'MessageAudioButton';
|
|||
class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioState> {
|
||||
static contextType = MessageContext;
|
||||
|
||||
private sound: any;
|
||||
private sound: Sound;
|
||||
|
||||
constructor(props: IMessageAudioProps) {
|
||||
super(props);
|
||||
|
@ -141,7 +142,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||
shouldComponentUpdate(nextProps: IMessageAudioProps, nextState: IMessageAudioState) {
|
||||
const { currentTime, duration, paused, loading } = this.state;
|
||||
const { file, theme } = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
|
@ -182,7 +183,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
}
|
||||
}
|
||||
|
||||
onPlaybackStatusUpdate = (status: any) => {
|
||||
onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
|
||||
if (status) {
|
||||
this.onLoad(status);
|
||||
this.onProgress(status);
|
||||
|
@ -190,20 +191,25 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
}
|
||||
};
|
||||
|
||||
onLoad = (data: any) => {
|
||||
onLoad = (data: AVPlaybackStatus) => {
|
||||
if (data.isLoaded && data.durationMillis) {
|
||||
const duration = data.durationMillis / 1000;
|
||||
this.setState({ duration: duration > 0 ? duration : 0 });
|
||||
}
|
||||
};
|
||||
|
||||
onProgress = (data: any) => {
|
||||
onProgress = (data: AVPlaybackStatus) => {
|
||||
if (data.isLoaded) {
|
||||
const { duration } = this.state;
|
||||
const currentTime = data.positionMillis / 1000;
|
||||
if (currentTime <= duration) {
|
||||
this.setState({ currentTime });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onEnd = async (data: any) => {
|
||||
onEnd = async (data: AVPlaybackStatus) => {
|
||||
if (data.isLoaded) {
|
||||
if (data.didJustFinish) {
|
||||
try {
|
||||
await this.sound.stopAsync();
|
||||
|
@ -212,6 +218,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
get duration() {
|
||||
|
@ -238,7 +245,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
}
|
||||
};
|
||||
|
||||
onValueChange = async (value: any) => {
|
||||
onValueChange = async (value: number) => {
|
||||
try {
|
||||
this.setState({ currentTime: value });
|
||||
await this.sound.setPositionAsync(value * 1000);
|
||||
|
|
|
@ -8,7 +8,8 @@ const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks
|
|||
const appId = blocks[0]?.appId || '';
|
||||
return React.createElement(
|
||||
messageBlockWithContext({
|
||||
action: async ({ actionId, value, blockId }: any) => {
|
||||
action: async ({ actionId, value, blockId }: { actionId: string; value: string; blockId: string }) => {
|
||||
if (blockAction) {
|
||||
await blockAction({
|
||||
actionId,
|
||||
appId,
|
||||
|
@ -17,6 +18,7 @@ const Blocks = React.memo(({ blocks, id: mid, rid, blockAction }: IMessageBlocks
|
|||
rid,
|
||||
mid
|
||||
});
|
||||
}
|
||||
},
|
||||
appId,
|
||||
rid
|
||||
|
|
|
@ -9,10 +9,13 @@ import I18n from '../../i18n';
|
|||
import { themes } from '../../constants/colors';
|
||||
import MessageContext from './Context';
|
||||
import { IMessageBroadcast } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const Broadcast = React.memo(({ author, broadcast, theme }: IMessageBroadcast) => {
|
||||
const Broadcast = React.memo(({ author, broadcast }: IMessageBroadcast) => {
|
||||
const { user, replyBroadcast } = useContext(MessageContext);
|
||||
const isOwn = author._id === user.id;
|
||||
const { theme } = useTheme();
|
||||
const isOwn = author?._id === user.id;
|
||||
|
||||
if (broadcast && !isOwn) {
|
||||
return (
|
||||
<View style={styles.buttonContainer}>
|
||||
|
|
|
@ -8,8 +8,11 @@ import I18n from '../../i18n';
|
|||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { IMessageCallButton } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const CallButton = React.memo(({ theme, callJitsi }: IMessageCallButton) => (
|
||||
const CallButton = React.memo(({ callJitsi }: IMessageCallButton) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={styles.buttonContainer}>
|
||||
<Touchable
|
||||
onPress={callJitsi}
|
||||
|
@ -22,7 +25,8 @@ const CallButton = React.memo(({ theme, callJitsi }: IMessageCallButton) => (
|
|||
</>
|
||||
</Touchable>
|
||||
</View>
|
||||
));
|
||||
);
|
||||
});
|
||||
|
||||
CallButton.displayName = 'CallButton';
|
||||
|
||||
|
|
|
@ -4,19 +4,6 @@ import React from 'react';
|
|||
import MessageContext from '../../Context';
|
||||
import CollapsibleQuote from '.';
|
||||
|
||||
// For some reason a general mock didn't work, I have to do a search
|
||||
jest.mock('react-native-mmkv-storage', () => ({
|
||||
Loader: jest.fn().mockImplementation(() => ({
|
||||
setProcessingMode: jest.fn().mockImplementation(() => ({
|
||||
withEncryption: jest.fn().mockImplementation(() => ({
|
||||
initialize: jest.fn()
|
||||
}))
|
||||
}))
|
||||
})),
|
||||
create: jest.fn(),
|
||||
MODES: { MULTI_PROCESS: '' }
|
||||
}));
|
||||
|
||||
const testAttachment = {
|
||||
ts: '1970-01-01T00:00:00.000Z',
|
||||
title: 'Engineering (9 today)',
|
||||
|
|
|
@ -10,17 +10,19 @@ import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
|||
import { themes } from '../../constants/colors';
|
||||
import MessageContext from './Context';
|
||||
import Encrypted from './Encrypted';
|
||||
import { E2E_MESSAGE_TYPE } from '../../lib/encryption/constants';
|
||||
import { E2E_MESSAGE_TYPE } from '../../lib/constants';
|
||||
import { IMessageContent } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const Content = React.memo(
|
||||
(props: IMessageContent) => {
|
||||
const { theme } = useTheme();
|
||||
if (props.isInfo) {
|
||||
// @ts-ignore
|
||||
const infoMessage = getInfoMessage({ ...props });
|
||||
|
||||
const renderMessageContent = (
|
||||
<Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]} accessibilityLabel={infoMessage}>
|
||||
<Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]} accessibilityLabel={infoMessage}>
|
||||
{infoMessage}
|
||||
</Text>
|
||||
);
|
||||
|
@ -36,14 +38,12 @@ const Content = React.memo(
|
|||
return renderMessageContent;
|
||||
}
|
||||
|
||||
const isPreview: any = props.tmid && !props.isThreadRoom;
|
||||
const isPreview = props.tmid && !props.isThreadRoom;
|
||||
let content = null;
|
||||
|
||||
if (props.isEncrypted) {
|
||||
content = (
|
||||
<Text
|
||||
style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}
|
||||
accessibilityLabel={I18n.t('Encrypted_message')}>
|
||||
<Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]} accessibilityLabel={I18n.t('Encrypted_message')}>
|
||||
{I18n.t('Encrypted_message')}
|
||||
</Text>
|
||||
);
|
||||
|
@ -65,7 +65,7 @@ const Content = React.memo(
|
|||
navToRoomInfo={props.navToRoomInfo}
|
||||
tmid={props.tmid}
|
||||
useRealName={props.useRealName}
|
||||
theme={props.theme}
|
||||
theme={theme}
|
||||
onLinkPress={onLinkPress}
|
||||
/>
|
||||
);
|
||||
|
@ -76,13 +76,13 @@ const Content = React.memo(
|
|||
content = (
|
||||
<View style={styles.flex}>
|
||||
<View style={styles.contentContainer}>{content}</View>
|
||||
<Encrypted type={props.type} theme={props.theme} />
|
||||
<Encrypted type={props.type} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (props.isIgnored) {
|
||||
content = <Text style={[styles.textInfo, { color: themes[props.theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
|
||||
content = <Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
|
||||
}
|
||||
|
||||
return <View style={props.isTemp && styles.temp}>{content}</View>;
|
||||
|
@ -97,9 +97,6 @@ const Content = React.memo(
|
|||
if (prevProps.type !== nextProps.type) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.theme !== nextProps.theme) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.isEncrypted !== nextProps.isEncrypted) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@ import { DISCUSSION } from './constants';
|
|||
import { themes } from '../../constants/colors';
|
||||
import MessageContext from './Context';
|
||||
import { formatDateThreads } from '../../utils/room';
|
||||
import { IMessageDiscussion } from './interfaces';
|
||||
import { IMessage } from '../../definitions';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const Discussion = React.memo(
|
||||
({ msg, dcount, dlm, theme }: IMessageDiscussion) => {
|
||||
({ msg, dcount, dlm }: Pick<IMessage, 'msg' | 'dcount' | 'dlm'>) => {
|
||||
const { theme } = useTheme();
|
||||
let time;
|
||||
if (dlm) {
|
||||
time = formatDateThreads(dlm);
|
||||
|
@ -50,9 +52,6 @@ const Discussion = React.memo(
|
|||
if (prevProps.dlm !== nextProps.dlm) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.theme !== nextProps.theme) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import React, { useContext } from 'react';
|
||||
|
||||
import Touchable from './Touchable';
|
||||
import { E2E_MESSAGE_TYPE } from '../../lib/encryption/constants';
|
||||
import { E2E_MESSAGE_TYPE } from '../../lib/constants';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { BUTTON_HIT_SLOP } from './utils';
|
||||
import MessageContext from './Context';
|
||||
import styles from './styles';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
interface IMessageEncrypted {
|
||||
type: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const Encrypted = React.memo(({ type, theme }: IMessageEncrypted) => {
|
||||
const Encrypted = React.memo(({ type }: { type: string }) => {
|
||||
const { theme } = useTheme();
|
||||
if (type !== E2E_MESSAGE_TYPE) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -13,32 +13,27 @@ import { themes } from '../../constants/colors';
|
|||
import MessageContext from './Context';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import { IAttachment } from '../../definitions';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
type TMessageButton = {
|
||||
children: JSX.Element;
|
||||
interface IMessageButton {
|
||||
children: React.ReactElement;
|
||||
disabled?: boolean;
|
||||
onPress: Function;
|
||||
onPress: () => void;
|
||||
theme: string;
|
||||
};
|
||||
|
||||
type TMessageImage = {
|
||||
img: string;
|
||||
theme: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface IMessageImage {
|
||||
file: IAttachment;
|
||||
imageUrl?: string;
|
||||
showAttachment?: Function;
|
||||
showAttachment?: (file: IAttachment) => void;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
isReply?: boolean;
|
||||
theme: string;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
getCustomEmoji?: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
const ImageProgress = createImageProgress(FastImage);
|
||||
|
||||
const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButton) => (
|
||||
const Button = React.memo(({ children, onPress, disabled, theme }: IMessageButton) => (
|
||||
<Touchable
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
|
@ -48,10 +43,10 @@ const Button = React.memo(({ children, onPress, disabled, theme }: TMessageButto
|
|||
</Touchable>
|
||||
));
|
||||
|
||||
export const MessageImage = React.memo(({ img, theme }: TMessageImage) => (
|
||||
export const MessageImage = React.memo(({ imgUri, theme }: { imgUri: string; theme: string }) => (
|
||||
<ImageProgress
|
||||
style={[styles.image, { borderColor: themes[theme].borderColor }]}
|
||||
source={{ uri: encodeURI(img) }}
|
||||
source={{ uri: encodeURI(imgUri) }}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
indicator={Progress.Pie}
|
||||
indicatorProps={{
|
||||
|
@ -61,9 +56,11 @@ export const MessageImage = React.memo(({ img, theme }: TMessageImage) => (
|
|||
));
|
||||
|
||||
const ImageContainer = React.memo(
|
||||
({ file, imageUrl, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageImage) => {
|
||||
({ file, imageUrl, showAttachment, getCustomEmoji, style, isReply }: IMessageImage) => {
|
||||
const { theme } = useTheme();
|
||||
const { baseUrl, user } = useContext(MessageContext);
|
||||
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
||||
|
||||
if (!img) {
|
||||
return null;
|
||||
}
|
||||
|
@ -88,7 +85,7 @@ const ImageContainer = React.memo(
|
|||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
<MessageImage img={img} theme={theme} />
|
||||
<MessageImage imgUri={img} theme={theme} />
|
||||
</View>
|
||||
</Button>
|
||||
);
|
||||
|
@ -96,11 +93,11 @@ const ImageContainer = React.memo(
|
|||
|
||||
return (
|
||||
<Button disabled={isReply} theme={theme} onPress={onPress}>
|
||||
<MessageImage img={img} theme={theme} />
|
||||
<MessageImage imgUri={img} theme={theme} />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme
|
||||
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file)
|
||||
);
|
||||
|
||||
ImageContainer.displayName = 'MessageImageContainer';
|
||||
|
|
|
@ -19,6 +19,7 @@ import ReadReceipt from './ReadReceipt';
|
|||
import CallButton from './CallButton';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const MessageInner = React.memo((props: IMessageInner) => {
|
||||
const { attachments } = props;
|
||||
|
@ -85,7 +86,6 @@ const Message = React.memo((props: IMessage) => {
|
|||
<View style={[styles.container, props.style]}>
|
||||
{thread}
|
||||
<View style={styles.flex}>
|
||||
{/* @ts-ignore */}
|
||||
<MessageAvatar small {...props} />
|
||||
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
|
||||
<Content {...props} />
|
||||
|
@ -98,12 +98,11 @@ const Message = React.memo((props: IMessage) => {
|
|||
return (
|
||||
<View style={[styles.container, props.style]}>
|
||||
<View style={styles.flex}>
|
||||
{/* @ts-ignore */}
|
||||
<MessageAvatar {...props} />
|
||||
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
|
||||
<MessageInner {...props} />
|
||||
</View>
|
||||
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} theme={props.theme} />
|
||||
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
@ -119,12 +118,14 @@ const MessageTouchable = React.memo((props: IMessageTouchable & IMessage) => {
|
|||
);
|
||||
}
|
||||
const { onPress, onLongPress } = useContext(MessageContext);
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<Touchable
|
||||
onLongPress={onLongPress}
|
||||
onPress={onPress}
|
||||
disabled={(props.isInfo && !props.isThreadReply) || props.archived || props.isTemp || props.type === 'jitsi_call_started'}
|
||||
style={{ backgroundColor: props.highlighted ? themes[props.theme].headerBackground : null }}>
|
||||
style={{ backgroundColor: props.highlighted ? themes[theme].headerBackground : null }}>
|
||||
<View>
|
||||
<Message {...props} />
|
||||
</View>
|
||||
|
|
|
@ -4,13 +4,13 @@ import Avatar from '../Avatar';
|
|||
import styles from './styles';
|
||||
import MessageContext from './Context';
|
||||
import { IMessageAvatar } from './interfaces';
|
||||
import { SubscriptionType } from '../../definitions';
|
||||
|
||||
const MessageAvatar = React.memo(
|
||||
({ isHeader, avatar, author, small, navToRoomInfo, emoji, getCustomEmoji, theme }: IMessageAvatar) => {
|
||||
const MessageAvatar = React.memo(({ isHeader, avatar, author, small, navToRoomInfo, emoji, getCustomEmoji }: IMessageAvatar) => {
|
||||
const { user } = useContext(MessageContext);
|
||||
if (isHeader && author) {
|
||||
const navParam = {
|
||||
t: 'd',
|
||||
t: SubscriptionType.DIRECT,
|
||||
rid: author._id
|
||||
};
|
||||
return (
|
||||
|
@ -23,13 +23,11 @@ const MessageAvatar = React.memo(
|
|||
getCustomEmoji={getCustomEmoji}
|
||||
avatar={avatar}
|
||||
emoji={emoji}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
MessageAvatar.displayName = 'MessageAvatar';
|
||||
|
||||
|
|
|
@ -6,14 +6,12 @@ import styles from './styles';
|
|||
import { BUTTON_HIT_SLOP } from './utils';
|
||||
import { themes } from '../../constants/colors';
|
||||
import MessageContext from './Context';
|
||||
|
||||
interface IMessageError {
|
||||
hasError: boolean;
|
||||
theme: string;
|
||||
}
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const MessageError = React.memo(
|
||||
({ hasError, theme }: IMessageError) => {
|
||||
({ hasError }: { hasError: boolean }) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!hasError) {
|
||||
return null;
|
||||
}
|
||||
|
@ -24,7 +22,7 @@ const MessageError = React.memo(
|
|||
</Touchable>
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => prevProps.hasError === nextProps.hasError && prevProps.theme === nextProps.theme
|
||||
(prevProps, nextProps) => prevProps.hasError === nextProps.hasError
|
||||
);
|
||||
|
||||
MessageError.displayName = 'MessageError';
|
||||
|
|
|
@ -7,30 +7,28 @@ import styles from './styles';
|
|||
import Emoji from './Emoji';
|
||||
import { BUTTON_HIT_SLOP } from './utils';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
import MessageContext from './Context';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
|
||||
interface IMessageAddReaction {
|
||||
theme: string;
|
||||
interface IReaction {
|
||||
_id: string;
|
||||
emoji: string;
|
||||
usernames: string[];
|
||||
}
|
||||
|
||||
interface IMessageReaction {
|
||||
reaction: {
|
||||
usernames: [];
|
||||
emoji: object;
|
||||
};
|
||||
reaction: IReaction;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IMessageReactions {
|
||||
reactions?: object[];
|
||||
reactions?: IReaction[];
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const AddReaction = React.memo(({ theme }: IMessageAddReaction) => {
|
||||
const AddReaction = React.memo(({ theme }: { theme: string }) => {
|
||||
const { reactionInit } = useContext(MessageContext);
|
||||
return (
|
||||
<Touchable
|
||||
|
@ -49,7 +47,7 @@ const AddReaction = React.memo(({ theme }: IMessageAddReaction) => {
|
|||
|
||||
const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReaction) => {
|
||||
const { onReactionPress, onReactionLongPress, baseUrl, user } = useContext(MessageContext);
|
||||
const reacted = reaction.usernames.findIndex((item: IMessageReaction) => item === user.username) !== -1;
|
||||
const reacted = reaction.usernames.findIndex((item: string) => item === user.username) !== -1;
|
||||
return (
|
||||
<Touchable
|
||||
onPress={() => onReactionPress(reaction.emoji)}
|
||||
|
@ -76,13 +74,15 @@ const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReacti
|
|||
);
|
||||
});
|
||||
|
||||
const Reactions = React.memo(({ reactions, getCustomEmoji, theme }: IMessageReactions) => {
|
||||
const Reactions = React.memo(({ reactions, getCustomEmoji }: IMessageReactions) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!Array.isArray(reactions) || reactions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<View style={styles.reactionsContainer}>
|
||||
{reactions.map((reaction: any) => (
|
||||
{reactions.map(reaction => (
|
||||
<Reaction key={reaction.emoji} reaction={reaction} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||
))}
|
||||
<AddReaction theme={theme} />
|
||||
|
@ -94,4 +94,4 @@ Reaction.displayName = 'MessageReaction';
|
|||
Reactions.displayName = 'MessageReactions';
|
||||
AddReaction.displayName = 'MessageAddReaction';
|
||||
|
||||
export default withTheme(Reactions);
|
||||
export default Reactions;
|
||||
|
|
|
@ -3,14 +3,10 @@ import React from 'react';
|
|||
import { themes } from '../../constants/colors';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import styles from './styles';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
interface IMessageReadReceipt {
|
||||
isReadReceiptEnabled: boolean;
|
||||
unread: boolean;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread, theme }: IMessageReadReceipt) => {
|
||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
||||
const { theme } = useTheme();
|
||||
if (isReadReceiptEnabled && !unread && unread !== null) {
|
||||
return <CustomIcon name='check' color={themes[theme].tintColor} size={15} style={styles.readReceipt} />;
|
||||
}
|
||||
|
|
|
@ -7,15 +7,18 @@ import { themes } from '../../constants/colors';
|
|||
import I18n from '../../i18n';
|
||||
import { MarkdownPreview } from '../markdown';
|
||||
import { IMessageRepliedThread } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted }: IMessageRepliedThread) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
|
||||
if (!tmid || !isHeader) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [msg, setMsg] = useState(isEncrypted ? I18n.t('Encrypted_message') : tmsg);
|
||||
const fetch = async () => {
|
||||
const threadName = await fetchThreadName(tmid, id);
|
||||
const threadName = fetchThreadName ? await fetchThreadName(tmid, id) : '';
|
||||
setMsg(threadName);
|
||||
};
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ const styles = StyleSheet.create({
|
|||
marginBottom: 4
|
||||
},
|
||||
image: {
|
||||
// @ts-ignore
|
||||
// @ts-ignore TODO - check with the team, change this to undefined
|
||||
width: null,
|
||||
height: 200,
|
||||
flex: 1,
|
||||
|
@ -93,24 +93,6 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
interface IMessageTitle {
|
||||
attachment: IAttachment;
|
||||
timeFormat?: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IMessageDescription {
|
||||
attachment: IAttachment;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IMessageFields {
|
||||
attachment: IAttachment;
|
||||
theme: string;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
interface IMessageReply {
|
||||
attachment: IAttachment;
|
||||
timeFormat?: string;
|
||||
|
@ -118,7 +100,7 @@ interface IMessageReply {
|
|||
getCustomEmoji: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
||||
const Title = React.memo(({ attachment, timeFormat, theme }: { attachment: IAttachment; timeFormat?: string; theme: string }) => {
|
||||
const time = attachment.message_link && attachment.ts ? moment(attachment.ts).format(timeFormat) : null;
|
||||
return (
|
||||
<View style={styles.authorContainer}>
|
||||
|
@ -132,7 +114,7 @@ const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
|||
});
|
||||
|
||||
const Description = React.memo(
|
||||
({ attachment, getCustomEmoji, theme }: IMessageDescription) => {
|
||||
({ attachment, getCustomEmoji, theme }: { attachment: IAttachment; getCustomEmoji: TGetCustomEmoji; theme: string }) => {
|
||||
const text = attachment.text || attachment.title;
|
||||
if (!text) {
|
||||
return null;
|
||||
|
@ -164,7 +146,7 @@ const Description = React.memo(
|
|||
);
|
||||
|
||||
const UrlImage = React.memo(
|
||||
({ image }: any) => {
|
||||
({ image }: { image?: string }) => {
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
|
@ -176,7 +158,7 @@ const UrlImage = React.memo(
|
|||
);
|
||||
|
||||
const Fields = React.memo(
|
||||
({ attachment, theme, getCustomEmoji }: IMessageFields) => {
|
||||
({ attachment, theme, getCustomEmoji }: { attachment: IAttachment; theme: string; getCustomEmoji: TGetCustomEmoji }) => {
|
||||
if (!attachment.fields) {
|
||||
return null;
|
||||
}
|
||||
|
@ -206,12 +188,12 @@ const Fields = React.memo(
|
|||
const Reply = React.memo(
|
||||
({ attachment, timeFormat, index, getCustomEmoji }: IMessageReply) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!attachment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { theme } = useTheme();
|
||||
const { baseUrl, user, jumpToMessage } = useContext(MessageContext);
|
||||
|
||||
const onPress = async () => {
|
||||
|
|
|
@ -7,9 +7,12 @@ import MessageContext from './Context';
|
|||
import ThreadDetails from '../ThreadDetails';
|
||||
import I18n from '../../i18n';
|
||||
import { IMessageThread } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const Thread = React.memo(
|
||||
({ msg, tcount, tlm, isThreadRoom, theme, id }: IMessageThread) => {
|
||||
({ msg, tcount, tlm, isThreadRoom, id }: IMessageThread) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!tlm || isThreadRoom || tcount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -38,9 +41,6 @@ const Thread = React.memo(
|
|||
if (prevProps.tcount !== nextProps.tcount) {
|
||||
return false;
|
||||
}
|
||||
if (prevProps.theme !== nextProps.theme) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Clipboard, StyleSheet, Text, View } from 'react-native';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||
import { dequal } from 'dequal';
|
||||
|
||||
|
@ -7,11 +8,12 @@ import Touchable from './Touchable';
|
|||
import openLink from '../../utils/openLink';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme, withTheme } from '../../theme';
|
||||
import { LISTENER } from '../Toast';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import I18n from '../../i18n';
|
||||
import MessageContext from './Context';
|
||||
import { IUrl } from '../../definitions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
|
@ -49,29 +51,6 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
interface IMessageUrlContent {
|
||||
title: string;
|
||||
description: string;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IMessageUrl {
|
||||
url: {
|
||||
ignoreParse: boolean;
|
||||
url: string;
|
||||
image: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
index: number;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface IMessageUrls {
|
||||
urls?: any;
|
||||
theme?: string;
|
||||
}
|
||||
|
||||
const UrlImage = React.memo(
|
||||
({ image }: { image: string }) => {
|
||||
if (!image) {
|
||||
|
@ -85,7 +64,7 @@ const UrlImage = React.memo(
|
|||
);
|
||||
|
||||
const UrlContent = React.memo(
|
||||
({ title, description, theme }: IMessageUrlContent) => (
|
||||
({ title, description, theme }: { title: string; description: string; theme: string }) => (
|
||||
<View style={styles.textContainer}>
|
||||
{title ? (
|
||||
<Text style={[styles.title, { color: themes[theme].tintColor }]} numberOfLines={2}>
|
||||
|
@ -114,7 +93,7 @@ const UrlContent = React.memo(
|
|||
);
|
||||
|
||||
const Url = React.memo(
|
||||
({ url, index, theme }: IMessageUrl) => {
|
||||
({ url, index, theme }: { url: IUrl; index: number; theme: string }) => {
|
||||
if (!url || url?.ignoreParse) {
|
||||
return null;
|
||||
}
|
||||
|
@ -151,14 +130,17 @@ const Url = React.memo(
|
|||
);
|
||||
|
||||
const Urls = React.memo(
|
||||
({ urls, theme }: IMessageUrls) => {
|
||||
// TODO - didn't work - (React.ReactElement | null)[] | React.ReactElement | null
|
||||
({ urls }: { urls?: IUrl[] }): any => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!urls || urls.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return urls.map((url: any, index: number) => <Url url={url} key={url.url} index={index} theme={theme!} />);
|
||||
return urls.map((url: IUrl, index: number) => <Url url={url} key={url.url} index={index} theme={theme} />);
|
||||
},
|
||||
(oldProps, newProps) => dequal(oldProps.urls, newProps.urls) && oldProps.theme === newProps.theme
|
||||
(oldProps, newProps) => dequal(oldProps.urls, newProps.urls)
|
||||
);
|
||||
|
||||
UrlImage.displayName = 'MessageUrlImage';
|
||||
|
|
|
@ -3,12 +3,14 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|||
import moment from 'moment';
|
||||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
import MessageError from './MessageError';
|
||||
import sharedStyles from '../../views/Styles';
|
||||
import messageStyles from './styles';
|
||||
import MessageContext from './Context';
|
||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
||||
import { SubscriptionType } from '../../definitions';
|
||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -49,15 +51,15 @@ interface IMessageUser {
|
|||
alias?: string;
|
||||
ts?: Date;
|
||||
timeFormat?: string;
|
||||
theme: string;
|
||||
navToRoomInfo?: Function;
|
||||
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
||||
type: string;
|
||||
}
|
||||
|
||||
const User = React.memo(
|
||||
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, type, ...props }: IMessageUser) => {
|
||||
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => {
|
||||
if (isHeader || hasError) {
|
||||
const { user } = useContext(MessageContext);
|
||||
const { theme } = useTheme();
|
||||
const username = (useRealName && author?.name) || author?.username;
|
||||
const aliasUsername = alias ? (
|
||||
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>
|
||||
|
@ -65,8 +67,8 @@ const User = React.memo(
|
|||
const time = moment(ts).format(timeFormat);
|
||||
const onUserPress = () => {
|
||||
navToRoomInfo?.({
|
||||
t: 'd',
|
||||
rid: author?._id
|
||||
t: SubscriptionType.DIRECT,
|
||||
rid: author?._id || ''
|
||||
});
|
||||
};
|
||||
const isDisabled = author?._id === user.id;
|
||||
|
@ -83,7 +85,7 @@ const User = React.memo(
|
|||
<Text
|
||||
style={[styles.usernameInfoMessage, { color: themes[theme].titleText }]}
|
||||
onPress={onUserPress}
|
||||
// @ts-ignore
|
||||
// @ts-ignore // TODO - check this prop
|
||||
disabled={isDisabled}>
|
||||
{textContent}
|
||||
</Text>
|
||||
|
@ -98,7 +100,7 @@ const User = React.memo(
|
|||
</Text>
|
||||
</TouchableOpacity>
|
||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
|
||||
{hasError && <MessageError hasError={hasError} theme={theme} {...props} />}
|
||||
{hasError ? <MessageError hasError={hasError} {...props} /> : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -108,4 +110,4 @@ const User = React.memo(
|
|||
|
||||
User.displayName = 'MessageUser';
|
||||
|
||||
export default withTheme(User);
|
||||
export default User;
|
||||
|
|
|
@ -16,9 +16,10 @@ import I18n from '../../i18n';
|
|||
import { IAttachment } from '../../definitions/IAttachment';
|
||||
import RCActivityIndicator from '../ActivityIndicator';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
||||
const isTypeSupported = (type: any) => SUPPORTED_TYPES.indexOf(type) !== -1;
|
||||
const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
|
@ -33,23 +34,24 @@ const styles = StyleSheet.create({
|
|||
|
||||
interface IMessageVideo {
|
||||
file: IAttachment;
|
||||
showAttachment?: Function;
|
||||
showAttachment?: (file: IAttachment) => void;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
isReply?: boolean;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
const Video = React.memo(
|
||||
({ file, showAttachment, getCustomEmoji, style, isReply, theme }: IMessageVideo) => {
|
||||
({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => {
|
||||
const { baseUrl, user } = useContext(MessageContext);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { theme } = useTheme();
|
||||
|
||||
if (!baseUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onPress = async () => {
|
||||
if (isTypeSupported(file.video_type) && showAttachment) {
|
||||
if (file.video_type && isTypeSupported(file.video_type) && showAttachment) {
|
||||
return showAttachment(file);
|
||||
}
|
||||
|
||||
|
@ -93,7 +95,7 @@ const Video = React.memo(
|
|||
</>
|
||||
);
|
||||
},
|
||||
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file) && prevProps.theme === nextProps.theme
|
||||
(prevProps, nextProps) => dequal(prevProps.file, nextProps.file)
|
||||
);
|
||||
|
||||
export default Video;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import React from 'react';
|
||||
import { Keyboard, ViewStyle } from 'react-native';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import Message from './Message';
|
||||
import MessageContext from './Context';
|
||||
import debounce from '../../utils/debounce';
|
||||
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
|
||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
|
||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/constants';
|
||||
import messagesStatus from '../../constants/messagesStatus';
|
||||
import { withTheme } from '../../theme';
|
||||
import { useTheme, withTheme } from '../../theme';
|
||||
import openLink from '../../utils/openLink';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import { TAnyMessageModel } from '../../definitions';
|
||||
import { IAttachment, TAnyMessageModel } from '../../definitions';
|
||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||
|
||||
interface IMessageContainerProps {
|
||||
item: TAnyMessageModel;
|
||||
|
@ -20,7 +22,7 @@ interface IMessageContainerProps {
|
|||
token: string;
|
||||
};
|
||||
msg?: string;
|
||||
rid?: string;
|
||||
rid: string;
|
||||
timeFormat?: string;
|
||||
style?: ViewStyle;
|
||||
archived?: boolean;
|
||||
|
@ -37,44 +39,35 @@ interface IMessageContainerProps {
|
|||
isIgnored?: boolean;
|
||||
highlighted?: boolean;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
onLongPress?: Function;
|
||||
onReactionPress?: Function;
|
||||
onEncryptedPress?: Function;
|
||||
onDiscussionPress?: Function;
|
||||
onThreadPress?: Function;
|
||||
errorActionsShow?: Function;
|
||||
replyBroadcast?: Function;
|
||||
reactionInit?: Function;
|
||||
fetchThreadName?: Function;
|
||||
showAttachment?: Function;
|
||||
onReactionLongPress?: Function;
|
||||
navToRoomInfo?: Function;
|
||||
callJitsi?: Function;
|
||||
blockAction?: Function;
|
||||
onAnswerButtonPress?: Function;
|
||||
theme?: string;
|
||||
onLongPress?: (item: TAnyMessageModel) => void;
|
||||
onReactionPress?: (emoji: string, id: string) => void;
|
||||
onEncryptedPress?: () => void;
|
||||
onDiscussionPress?: (item: TAnyMessageModel) => void;
|
||||
onThreadPress?: (item: TAnyMessageModel) => void;
|
||||
errorActionsShow?: (item: TAnyMessageModel) => void;
|
||||
replyBroadcast?: (item: TAnyMessageModel) => void;
|
||||
reactionInit?: (item: TAnyMessageModel) => void;
|
||||
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
|
||||
showAttachment: (file: IAttachment) => void;
|
||||
onReactionLongPress?: (item: TAnyMessageModel) => void;
|
||||
navToRoomInfo: (navParam: IRoomInfoParam) => void;
|
||||
callJitsi?: () => void;
|
||||
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
|
||||
onAnswerButtonPress?: (message: string, tmid?: string, tshow?: boolean) => void;
|
||||
threadBadgeColor?: string;
|
||||
toggleFollowThread?: Function;
|
||||
jumpToMessage?: Function;
|
||||
onPress?: Function;
|
||||
toggleFollowThread?: (isFollowingThread: boolean, tmid?: string) => Promise<void>;
|
||||
jumpToMessage?: (link: string) => void;
|
||||
onPress?: () => void;
|
||||
}
|
||||
|
||||
class MessageContainer extends React.Component<IMessageContainerProps> {
|
||||
interface IMessageContainerState {
|
||||
isManualUnignored: boolean;
|
||||
}
|
||||
|
||||
class MessageContainer extends React.Component<IMessageContainerProps, IMessageContainerState> {
|
||||
static defaultProps = {
|
||||
getCustomEmoji: () => null,
|
||||
onLongPress: () => {},
|
||||
onReactionPress: () => {},
|
||||
onEncryptedPress: () => {},
|
||||
onDiscussionPress: () => {},
|
||||
onThreadPress: () => {},
|
||||
onAnswerButtonPress: () => {},
|
||||
errorActionsShow: () => {},
|
||||
replyBroadcast: () => {},
|
||||
reactionInit: () => {},
|
||||
fetchThreadName: () => {},
|
||||
showAttachment: () => {},
|
||||
onReactionLongPress: () => {},
|
||||
navToRoomInfo: () => {},
|
||||
callJitsi: () => {},
|
||||
blockAction: () => {},
|
||||
archived: false,
|
||||
|
@ -85,7 +78,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
|
||||
state = { isManualUnignored: false };
|
||||
|
||||
private subscription: any;
|
||||
private subscription?: Subscription;
|
||||
|
||||
componentDidMount() {
|
||||
const { item } = this.props;
|
||||
|
@ -97,12 +90,9 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: any, nextState: any) {
|
||||
shouldComponentUpdate(nextProps: IMessageContainerProps, nextState: IMessageContainerState) {
|
||||
const { isManualUnignored } = this.state;
|
||||
const { theme, threadBadgeColor, isIgnored, highlighted } = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
}
|
||||
const { threadBadgeColor, isIgnored, highlighted } = this.props;
|
||||
if (nextProps.highlighted !== highlighted) {
|
||||
return true;
|
||||
}
|
||||
|
@ -169,7 +159,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
}
|
||||
};
|
||||
|
||||
onReactionPress = (emoji: any) => {
|
||||
onReactionPress = (emoji: string) => {
|
||||
const { onReactionPress, item } = this.props;
|
||||
if (onReactionPress) {
|
||||
onReactionPress(emoji, item.id);
|
||||
|
@ -228,7 +218,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
previousItem.u.username === item.u.username &&
|
||||
!(previousItem.groupable === false || item.groupable === false || broadcast === true) &&
|
||||
// @ts-ignore TODO: IMessage vs IMessageFromServer non-sense
|
||||
item.ts - previousItem.ts < Message_GroupingPeriod! * 1000 &&
|
||||
item.ts - previousItem.ts < Message_GroupingPeriod * 1000 &&
|
||||
previousItem.tmid === item.tmid
|
||||
) {
|
||||
return false;
|
||||
|
@ -303,10 +293,11 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
};
|
||||
|
||||
onLinkPress = (link: string): void => {
|
||||
const { item, theme, jumpToMessage } = this.props;
|
||||
const isMessageLink = item?.attachments?.findIndex((att: any) => att?.message_link === link) !== -1;
|
||||
if (isMessageLink) {
|
||||
return jumpToMessage!(link);
|
||||
const { theme } = useTheme();
|
||||
const { item, jumpToMessage } = this.props;
|
||||
const isMessageLink = item?.attachments?.findIndex((att: IAttachment) => att?.message_link === link) !== -1;
|
||||
if (isMessageLink && jumpToMessage) {
|
||||
return jumpToMessage(link);
|
||||
}
|
||||
openLink(link, theme);
|
||||
};
|
||||
|
@ -332,7 +323,6 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
callJitsi,
|
||||
blockAction,
|
||||
rid,
|
||||
theme,
|
||||
threadBadgeColor,
|
||||
toggleFollowThread,
|
||||
jumpToMessage,
|
||||
|
@ -371,8 +361,8 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
let message = msg;
|
||||
// "autoTranslateRoom" and "autoTranslateLanguage" are properties from the subscription
|
||||
// "autoTranslateMessage" is a toggle between "View Original" and "Translate" state
|
||||
if (autoTranslateRoom && autoTranslateMessage) {
|
||||
message = getMessageTranslation(item, autoTranslateLanguage!) || message;
|
||||
if (autoTranslateRoom && autoTranslateMessage && autoTranslateLanguage) {
|
||||
message = getMessageTranslation(item, autoTranslateLanguage) || message;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -396,14 +386,15 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
toggleFollowThread,
|
||||
replies
|
||||
}}>
|
||||
{/* @ts-ignore*/}
|
||||
<Message
|
||||
id={id}
|
||||
msg={message}
|
||||
md={md}
|
||||
rid={rid!}
|
||||
rid={rid}
|
||||
author={u}
|
||||
ts={ts}
|
||||
type={t as any}
|
||||
type={t}
|
||||
attachments={attachments}
|
||||
blocks={blocks}
|
||||
urls={urls}
|
||||
|
@ -413,23 +404,20 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
emoji={emoji}
|
||||
timeFormat={timeFormat}
|
||||
style={style}
|
||||
archived={archived!}
|
||||
broadcast={broadcast!}
|
||||
archived={archived}
|
||||
broadcast={broadcast}
|
||||
useRealName={useRealName}
|
||||
isReadReceiptEnabled={isReadReceiptEnabled!}
|
||||
isReadReceiptEnabled={isReadReceiptEnabled}
|
||||
unread={unread}
|
||||
role={role}
|
||||
drid={drid}
|
||||
dcount={dcount}
|
||||
// @ts-ignore
|
||||
dlm={dlm}
|
||||
tmid={tmid}
|
||||
tcount={tcount}
|
||||
// @ts-ignore
|
||||
tlm={tlm}
|
||||
tmsg={tmsg}
|
||||
fetchThreadName={fetchThreadName!}
|
||||
// @ts-ignore
|
||||
fetchThreadName={fetchThreadName}
|
||||
mentions={mentions}
|
||||
channels={channels}
|
||||
isIgnored={this.isIgnored}
|
||||
|
@ -442,13 +430,12 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
|||
isTemp={this.isTemp}
|
||||
isEncrypted={this.isEncrypted}
|
||||
hasError={this.hasError}
|
||||
showAttachment={showAttachment!}
|
||||
showAttachment={showAttachment}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
navToRoomInfo={navToRoomInfo!}
|
||||
callJitsi={callJitsi!}
|
||||
blockAction={blockAction!}
|
||||
theme={theme as string}
|
||||
highlighted={highlighted!}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
callJitsi={callJitsi}
|
||||
blockAction={blockAction}
|
||||
highlighted={highlighted}
|
||||
/>
|
||||
</MessageContext.Provider>
|
||||
);
|
||||
|
|
|
@ -1,64 +1,45 @@
|
|||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||
import { StyleProp, TextStyle } from 'react-native';
|
||||
import { ImageStyle } from '@rocket.chat/react-native-fast-image';
|
||||
|
||||
import { IUserChannel, IUserMention } from '../markdown/interfaces';
|
||||
import { IUserChannel } from '../markdown/interfaces';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import { IAttachment } from '../../definitions';
|
||||
|
||||
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
|
||||
import { IAttachment, IThread, IUrl, IUserMention, IUserMessage, MessageType, TAnyMessageModel } from '../../definitions';
|
||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||
|
||||
export interface IMessageAttachments {
|
||||
attachments?: IAttachment[];
|
||||
timeFormat?: string;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
isReply?: boolean;
|
||||
showAttachment?: Function;
|
||||
showAttachment?: (file: IAttachment) => void;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
export interface IMessageAttachedActions {
|
||||
attachment: IAttachment;
|
||||
}
|
||||
|
||||
export interface IMessageAvatar {
|
||||
isHeader: boolean;
|
||||
avatar: string;
|
||||
emoji: string;
|
||||
author: {
|
||||
username: string;
|
||||
_id: string;
|
||||
};
|
||||
avatar?: string;
|
||||
emoji?: string;
|
||||
author?: IUserMessage;
|
||||
small?: boolean;
|
||||
navToRoomInfo: Function;
|
||||
navToRoomInfo: (navParam: IRoomInfoParam) => void;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export interface IMessageBlocks {
|
||||
blocks: any;
|
||||
blocks: { appId?: string }[];
|
||||
id: string;
|
||||
rid: string;
|
||||
blockAction: Function;
|
||||
blockAction?: (params: { actionId: string; appId: string; value: string; blockId: string; rid: string; mid: string }) => void;
|
||||
}
|
||||
|
||||
export interface IMessageBroadcast {
|
||||
author: {
|
||||
_id: string;
|
||||
};
|
||||
broadcast: boolean;
|
||||
theme: string;
|
||||
author?: IUserMessage;
|
||||
broadcast?: boolean;
|
||||
}
|
||||
|
||||
export interface IMessageCallButton {
|
||||
theme: string;
|
||||
callJitsi: Function;
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
id: string;
|
||||
username: string;
|
||||
token: string;
|
||||
name: string;
|
||||
callJitsi?: () => void;
|
||||
}
|
||||
|
||||
export interface IMessageContent {
|
||||
|
@ -69,40 +50,27 @@ export interface IMessageContent {
|
|||
isThreadRoom: boolean;
|
||||
msg?: string;
|
||||
md?: MarkdownAST;
|
||||
theme: string;
|
||||
isEdited: boolean;
|
||||
isEncrypted: boolean;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
channels?: IUserChannel[];
|
||||
mentions?: IUserMention[];
|
||||
navToRoomInfo?: Function;
|
||||
navToRoomInfo: (navParam: IRoomInfoParam) => void;
|
||||
useRealName?: boolean;
|
||||
isIgnored: boolean;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface IMessageDiscussion {
|
||||
msg?: string;
|
||||
dcount?: number;
|
||||
dlm?: Date;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
export interface IMessageEmoji {
|
||||
content: any;
|
||||
content: string;
|
||||
baseUrl: string;
|
||||
standardEmojiStyle: object;
|
||||
customEmojiStyle: object;
|
||||
standardEmojiStyle: { fontSize: number };
|
||||
customEmojiStyle: StyleProp<ImageStyle>;
|
||||
getCustomEmoji: TGetCustomEmoji;
|
||||
}
|
||||
|
||||
export interface IMessageThread {
|
||||
msg?: string;
|
||||
tcount?: number | null;
|
||||
theme: string;
|
||||
tlm?: Date;
|
||||
export interface IMessageThread extends Pick<IThread, 'msg' | 'tcount' | 'tlm' | 'id'> {
|
||||
isThreadRoom: boolean;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface IMessageTouchable {
|
||||
|
@ -110,40 +78,35 @@ export interface IMessageTouchable {
|
|||
isInfo: boolean;
|
||||
isThreadReply: boolean;
|
||||
isTemp: boolean;
|
||||
archived: boolean;
|
||||
highlighted: boolean;
|
||||
theme: string;
|
||||
ts?: any;
|
||||
urls?: any;
|
||||
archived?: boolean;
|
||||
highlighted?: boolean;
|
||||
ts?: string | Date;
|
||||
urls?: IUrl[];
|
||||
reactions?: any;
|
||||
alias?: any;
|
||||
role?: any;
|
||||
drid?: any;
|
||||
alias?: string;
|
||||
role?: string;
|
||||
drid?: string;
|
||||
}
|
||||
|
||||
export interface IMessageRepliedThread {
|
||||
tmid?: string;
|
||||
tmsg?: string;
|
||||
id: string;
|
||||
export interface IMessageRepliedThread extends Pick<IThread, 'tmid' | 'tmsg' | 'id'> {
|
||||
isHeader: boolean;
|
||||
theme: string;
|
||||
fetchThreadName: Function;
|
||||
fetchThreadName?: (tmid: string, id: string) => Promise<string | undefined>;
|
||||
isEncrypted: boolean;
|
||||
}
|
||||
|
||||
export interface IMessageInner
|
||||
extends IMessageDiscussion,
|
||||
IMessageContent,
|
||||
extends IMessageContent,
|
||||
IMessageCallButton,
|
||||
IMessageBlocks,
|
||||
IMessageThread,
|
||||
IMessageAttachments,
|
||||
IMessageBroadcast {
|
||||
type: TMessageType;
|
||||
type: MessageType;
|
||||
blocks: [];
|
||||
urls?: IUrl[];
|
||||
}
|
||||
|
||||
export interface IMessage extends IMessageRepliedThread, IMessageInner {
|
||||
export interface IMessage extends IMessageRepliedThread, IMessageInner, IMessageAvatar {
|
||||
isThreadReply: boolean;
|
||||
isThreadSequential: boolean;
|
||||
isInfo: boolean;
|
||||
|
@ -151,9 +114,11 @@ export interface IMessage extends IMessageRepliedThread, IMessageInner {
|
|||
isHeader: boolean;
|
||||
hasError: boolean;
|
||||
style: any;
|
||||
onLongPress: Function;
|
||||
isReadReceiptEnabled: boolean;
|
||||
// style: ViewStyle;
|
||||
onLongPress?: (item: TAnyMessageModel) => void;
|
||||
isReadReceiptEnabled?: boolean;
|
||||
unread?: boolean;
|
||||
theme: string;
|
||||
isIgnored: boolean;
|
||||
dcount: number | undefined;
|
||||
dlm: string | Date | undefined;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { IUser } from './IUser';
|
|||
|
||||
export interface IAttachment {
|
||||
ts?: string | Date;
|
||||
title: string;
|
||||
title?: string;
|
||||
type?: string;
|
||||
description?: string;
|
||||
title_link?: string;
|
||||
|
@ -19,7 +19,7 @@ export interface IAttachment {
|
|||
image_size?: number;
|
||||
author_name?: string;
|
||||
author_icon?: string;
|
||||
actions?: [];
|
||||
actions?: { type: string; msg: string; text: string }[];
|
||||
message_link?: string;
|
||||
text?: string;
|
||||
short?: boolean;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// TODO: evaluate unification with IEmoji
|
||||
export interface IEmoji {
|
||||
content: any;
|
||||
name: string;
|
||||
extension: any;
|
||||
isCustom: boolean;
|
||||
content?: string;
|
||||
name?: string;
|
||||
extension?: string;
|
||||
isCustom?: boolean;
|
||||
}
|
||||
|
||||
export interface ICustomEmoji {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Model from '@nozbe/watermelondb/Model';
|
||||
|
||||
// TODO: evaluate unification with IEmoji
|
||||
export interface IFrequentlyUsedEmoji {
|
||||
content?: string;
|
||||
extension?: string;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { IAttachment } from './IAttachment';
|
|||
import { IReaction } from './IReaction';
|
||||
import { TThreadMessageModel } from './IThreadMessage';
|
||||
import { TThreadModel } from './IThread';
|
||||
import { IUrlFromServer } from './IUrl';
|
||||
import { IUrl, IUrlFromServer } from './IUrl';
|
||||
|
||||
export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj' | MessageTypeLoad;
|
||||
|
||||
|
@ -75,7 +75,7 @@ export interface IMessageFromServer {
|
|||
ts: string | Date; // wm date issue
|
||||
u: IUserMessage;
|
||||
_updatedAt: string | Date;
|
||||
urls?: IUrlFromServer[];
|
||||
urls?: IUrl[];
|
||||
mentions?: IUserMention[];
|
||||
channels?: IUserChannel[];
|
||||
md?: MarkdownAST;
|
||||
|
@ -111,7 +111,7 @@ export interface ILoadMoreMessage {
|
|||
|
||||
export interface IMessage extends IMessageFromServer {
|
||||
id: string;
|
||||
t?: MessageType;
|
||||
t: MessageType;
|
||||
alias?: string;
|
||||
parseUrls?: boolean;
|
||||
avatar?: string;
|
||||
|
|
|
@ -106,6 +106,7 @@ export interface ISubscription {
|
|||
}
|
||||
|
||||
export type TSubscriptionModel = ISubscription & Model;
|
||||
export type TSubscription = TSubscriptionModel | ISubscription;
|
||||
|
||||
// https://github.com/RocketChat/Rocket.Chat/blob/a88a96fcadd925b678ff27ada37075e029f78b5e/definition/ISubscription.ts#L8
|
||||
export interface IServerSubscription extends IRocketChatRecord {
|
||||
|
|
|
@ -45,5 +45,4 @@ export interface IUrl extends IUrlFromServer {
|
|||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
url: string;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { RouteProp } from '@react-navigation/native';
|
|||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
import { TColors } from '../theme';
|
||||
|
||||
export * from './IAttachment';
|
||||
export * from './INotification';
|
||||
export * from './IPreferences';
|
||||
|
@ -32,8 +34,10 @@ export interface IBaseScreen<T extends Record<string, object | undefined>, S ext
|
|||
navigation: StackNavigationProp<T, S>;
|
||||
route: RouteProp<T, S>;
|
||||
dispatch: Dispatch;
|
||||
theme: string;
|
||||
isMasterDetail: boolean;
|
||||
// TODO: remove after migrating all Class components
|
||||
theme: string;
|
||||
colors: TColors;
|
||||
}
|
||||
|
||||
export * from './redux';
|
||||
|
|
|
@ -49,6 +49,9 @@ export type UsersEndpoints = {
|
|||
'users.resetAvatar': {
|
||||
POST: (params: { userId: string }) => {};
|
||||
};
|
||||
'users.removeOtherTokens': {
|
||||
POST: (params: { userId: string }) => {};
|
||||
};
|
||||
'users.getPreferences': {
|
||||
GET: (params: { userId: IUser['_id'] }) => {
|
||||
preferences: INotificationPreferences;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { TNavigationOptions } from './definitions/navigationTypes';
|
|||
export interface IDimensionsContextProps {
|
||||
width: number;
|
||||
height: number;
|
||||
scale?: number;
|
||||
scale: number;
|
||||
fontScale: number;
|
||||
setDimensions?: ({
|
||||
width,
|
||||
|
|
|
@ -9,6 +9,7 @@ import UnreadBadge from '../../../presentation/UnreadBadge';
|
|||
import RocketChat from '../../../lib/rocketchat';
|
||||
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../lib';
|
||||
import { IUser } from '../../../definitions/IUser';
|
||||
import Touch from '../../../utils/touch';
|
||||
|
||||
interface IOmnichannelStatus {
|
||||
searching: boolean;
|
||||
|
@ -48,7 +49,9 @@ const OmnichannelStatus = memo(({ searching, goQueue, queueSize, inquiryEnabled,
|
|||
right={() => (
|
||||
<View style={styles.omnichannelRightContainer}>
|
||||
{inquiryEnabled ? <UnreadBadge style={styles.queueIcon} unread={queueSize} /> : null}
|
||||
<Touch theme={theme} onPress={toggleLivechat}>
|
||||
<Switch value={status} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleLivechat} />
|
||||
</Touch>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -3,7 +3,6 @@ declare module 'commonmark';
|
|||
declare module 'commonmark-react-renderer';
|
||||
declare module 'remove-markdown';
|
||||
declare module 'react-native-image-progress';
|
||||
declare module 'react-native-platform-touchable';
|
||||
declare module 'react-native-ui-lib/keyboard';
|
||||
declare module '@rocket.chat/ui-kit';
|
||||
declare module '@rocket.chat/sdk';
|
||||
|
|
|
@ -14,7 +14,7 @@ import parseQuery from './lib/methods/helpers/parseQuery';
|
|||
import { initializePushNotifications, onNotification } from './notifications/push';
|
||||
import store from './lib/createStore';
|
||||
import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './utils/log';
|
||||
import { ThemeContext } from './theme';
|
||||
import { ThemeContext, TSupportedThemes } from './theme';
|
||||
import { DimensionsContext } from './dimensions';
|
||||
import RocketChat from './lib/rocketchat';
|
||||
import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './constants/tablet';
|
||||
|
@ -32,7 +32,7 @@ import { isFDroidBuild } from './constants/environment';
|
|||
import { IThemePreference } from './definitions/ITheme';
|
||||
import { ICommand } from './definitions/ICommand';
|
||||
import { initStore } from './lib/auxStore';
|
||||
import { themes } from './constants/colors';
|
||||
import { colors, themes } from './constants/colors';
|
||||
|
||||
RNScreens.enableScreens();
|
||||
initStore(store);
|
||||
|
@ -45,7 +45,7 @@ interface IDimensions {
|
|||
}
|
||||
|
||||
interface IState {
|
||||
theme: string;
|
||||
theme: TSupportedThemes;
|
||||
themePreferences: IThemePreference;
|
||||
width: number;
|
||||
height: number;
|
||||
|
@ -215,7 +215,8 @@ export default class Root extends React.Component<{}, IState> {
|
|||
value={{
|
||||
theme,
|
||||
themePreferences,
|
||||
setTheme: this.setTheme
|
||||
setTheme: this.setTheme,
|
||||
colors: colors[theme]
|
||||
}}>
|
||||
<DimensionsContext.Provider
|
||||
value={{
|
||||
|
|
|
@ -14,3 +14,8 @@ export const E2E_ROOM_TYPES: Record<string, string> = {
|
|||
d: 'd',
|
||||
p: 'p'
|
||||
};
|
||||
|
||||
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
|
||||
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
|
||||
export const ANALYTICS_EVENTS_KEY = 'RC_ANALYTICS_EVENTS_KEY';
|
||||
export const MIN_ROCKETCHAT_VERSION = '0.70.0';
|
|
@ -17,7 +17,7 @@ import {
|
|||
E2E_PUBLIC_KEY,
|
||||
E2E_RANDOM_PASSWORD_KEY,
|
||||
E2E_STATUS
|
||||
} from './constants';
|
||||
} from '../constants';
|
||||
import { joinVectorData, randomPassword, splitVectorData, toString, utf8ToBuffer } from './utils';
|
||||
import { EncryptionRoom } from './index';
|
||||
import { IMessage, ISubscription, TMessageModel, TSubscriptionModel, TThreadMessageModel, TThreadModel } from '../../definitions';
|
||||
|
|
|
@ -9,7 +9,7 @@ import Deferred from '../../utils/deferred';
|
|||
import debounce from '../../utils/debounce';
|
||||
import database from '../database';
|
||||
import log from '../../utils/log';
|
||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from './constants';
|
||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../constants';
|
||||
import {
|
||||
b64ToBuffer,
|
||||
bufferToB64,
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
import random from '../../utils/random';
|
||||
import { ITriggerAction, IUserInteraction, ModalActions } from '../../containers/UIKit/interfaces';
|
||||
import { TRocketChat } from '../../definitions/IRocketChat';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import fetch from '../../utils/fetch';
|
||||
import random from '../../utils/random';
|
||||
import Navigation from '../Navigation';
|
||||
import sdk from '../rocketchat/services/sdk';
|
||||
import {
|
||||
ActionTypes,
|
||||
ITriggerAction,
|
||||
ITriggerBlockAction,
|
||||
ITriggerCancel,
|
||||
ITriggerSubmitView,
|
||||
IUserInteraction,
|
||||
ModalActions
|
||||
} from '../../containers/UIKit/interfaces';
|
||||
import { TRocketChat } from '../../definitions/IRocketChat';
|
||||
|
||||
const triggersId = new Map();
|
||||
|
||||
|
@ -139,18 +131,3 @@ export function triggerAction(
|
|||
return reject();
|
||||
});
|
||||
}
|
||||
|
||||
export default function triggerBlockAction(this: TRocketChat, options: ITriggerBlockAction) {
|
||||
return triggerAction.call(this, { type: ActionTypes.ACTION, ...options });
|
||||
}
|
||||
|
||||
export async function triggerSubmitView(this: TRocketChat, { viewId, ...options }: ITriggerSubmitView) {
|
||||
const result = await triggerAction.call(this, { type: ActionTypes.SUBMIT, viewId, ...options });
|
||||
if (!result || ModalActions.CLOSE === result) {
|
||||
Navigation.back();
|
||||
}
|
||||
}
|
||||
|
||||
export function triggerCancel(this: TRocketChat, { view, ...options }: ITriggerCancel) {
|
||||
return triggerAction.call(this, { type: ActionTypes.CLOSED, view, ...options });
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import database from '../../database';
|
||||
import database from '../database';
|
||||
|
||||
export default async function clearCache({ server }: { server: string }): Promise<void> {
|
||||
try {
|
|
@ -1,5 +1,5 @@
|
|||
import { TSubscriptionModel } from '../../../definitions';
|
||||
import database from '../../database';
|
||||
import { TSubscriptionModel } from '../../definitions';
|
||||
import database from '../database';
|
||||
|
||||
export default async function getRoom(rid: string): Promise<TSubscriptionModel> {
|
||||
try {
|
|
@ -4,7 +4,7 @@ import { MessageTypeLoad } from '../../constants/messageTypeLoad';
|
|||
import { IMessage, TMessageModel } from '../../definitions';
|
||||
import log from '../../utils/log';
|
||||
import { getMessageById } from '../database/services/Message';
|
||||
import roomTypeToApiType, { RoomTypes } from '../rocketchat/methods/roomTypeToApiType';
|
||||
import roomTypeToApiType, { RoomTypes } from './roomTypeToApiType';
|
||||
import sdk from '../rocketchat/services/sdk';
|
||||
import { generateLoadMoreId } from '../utils';
|
||||
import updateMessages from './updateMessages';
|
||||
|
|
|
@ -8,7 +8,7 @@ import { MessageTypeLoad } from '../../constants/messageTypeLoad';
|
|||
import { generateLoadMoreId } from '../utils';
|
||||
import updateMessages from './updateMessages';
|
||||
import { TMessageModel } from '../../definitions';
|
||||
import RocketChat from '../rocketchat';
|
||||
import sdk from '../rocketchat/services/sdk';
|
||||
|
||||
const COUNT = 50;
|
||||
|
||||
|
@ -22,7 +22,7 @@ interface ILoadNextMessages {
|
|||
export default function loadNextMessages(args: ILoadNextMessages): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const data = await RocketChat.methodCallWrapper('loadNextMessages', args.rid, args.ts, COUNT);
|
||||
const data = await sdk.methodCallWrapper('loadNextMessages', args.rid, args.ts, COUNT);
|
||||
let messages = EJSON.fromJSONValue(data?.messages);
|
||||
messages = orderBy(messages, 'ts');
|
||||
if (messages?.length) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue