diff --git a/app/definitions/IMessage.ts b/app/definitions/IMessage.ts index bc8f793da6..fcbf90d5f8 100644 --- a/app/definitions/IMessage.ts +++ b/app/definitions/IMessage.ts @@ -3,7 +3,7 @@ import { MarkdownAST } from '@rocket.chat/message-parser'; import { IAttachment } from './IAttachment'; import { IReaction } from './IReaction'; -import { IUrl } from './IUrl'; +import { IUrlFromServer } from './IUrl'; export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj'; @@ -59,22 +59,61 @@ export interface ILastMessage { status: boolean; } -export interface IMessage { +interface IMessageFile { + _id: string; + name: string; + type: string; +} + +interface IMessageAttachment { + ts: string; + title: string; + title_link: string; + title_link_download: true; + image_dimensions: { + width: number; + height: number; + }; + image_preview: string; + image_url: string; + image_type: string; + image_size: number; + type: string; + description: string; +} + +export interface IMessageFromServer { + _id: string; + rid: string; + msg: string; + ts: string | Date; // wm date issue + u: IUserMessage; + _updatedAt: string | Date; + urls: IUrlFromServer[]; + mentions: IUserMention[]; + channels: IUserChannel[]; + md: MarkdownAST; + file: IMessageFile; + files: IMessageFile[]; + groupable: false; + attachments: IMessageAttachment[]; +} + +export interface ILoadMoreMessage { _id: string; rid: string; - msg?: string; + ts: string; + t: string; + msg: string; +} + +export interface IMessage extends IMessageFromServer { id: string; t?: MessageType; - ts: string | Date; - u: IUserMessage; alias?: string; parseUrls?: boolean; - groupable?: boolean; avatar?: string; emoji?: string; - attachments?: IAttachment[]; - urls?: IUrl[]; - _updatedAt: string | Date; status?: number; pinned?: boolean; starred?: boolean; @@ -88,8 +127,6 @@ export interface IMessage { tcount?: number; tlm?: string | Date; replies?: string[]; - mentions?: IUserMention[]; - channels?: IUserChannel[]; unread?: boolean; autoTranslate?: boolean; translations?: ITranslations[]; @@ -97,8 +134,9 @@ export interface IMessage { blocks?: any; e2e?: string; tshow?: boolean; - md?: MarkdownAST; subscription?: { id: string }; } export type TMessageModel = IMessage & Model; + +export type TTypeMessages = IMessageFromServer | ILoadMoreMessage | IMessage; diff --git a/app/definitions/IThread.ts b/app/definitions/IThread.ts index 824b2544e0..1294981c68 100644 --- a/app/definitions/IThread.ts +++ b/app/definitions/IThread.ts @@ -30,6 +30,7 @@ export interface IThreadResult { channels?: IUserChannel[]; replies?: string[]; tcount?: number; + status?: string; tlm?: string | Date; } diff --git a/app/definitions/IUrl.ts b/app/definitions/IUrl.ts index 3e613bab39..3210afa234 100644 --- a/app/definitions/IUrl.ts +++ b/app/definitions/IUrl.ts @@ -1,11 +1,3 @@ -export interface IUrl { - _id: number; - title: string; - description: string; - image: string; - url: string; -} - export interface IUrlFromServer { url: string; meta: { @@ -47,3 +39,11 @@ export interface IUrlFromServer { }; ignoreParse: boolean; } + +export interface IUrl extends IUrlFromServer { + _id: number; + title: string; + description: string; + image: string; + url: string; +} diff --git a/app/definitions/rest/v1/channels.ts b/app/definitions/rest/v1/channels.ts index d16798774e..e507d970e2 100644 --- a/app/definitions/rest/v1/channels.ts +++ b/app/definitions/rest/v1/channels.ts @@ -1,10 +1,16 @@ -import type { IMessage } from '../../IMessage'; +import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IRoom } from '../../IRoom'; import type { IUser } from '../../IUser'; export type ChannelsEndpoints = { 'channels.files': { - GET: (params: { roomId: IRoom['_id']; offset: number; count: number; sort: string; query: string }) => { + GET: (params: { + roomId: IRoom['_id']; + offset: number; + count: number; + sort: string | { uploadedAt: number }; + query: string; + }) => { files: IMessage[]; total: number; }; @@ -17,4 +23,9 @@ export type ChannelsEndpoints = { total: number; }; }; + 'channels.history': { + GET: (params: { roomId: string; count: number; latest?: string }) => { + messages: IMessageFromServer[]; + }; + }; }; diff --git a/app/definitions/rest/v1/groups.ts b/app/definitions/rest/v1/groups.ts index ca6454734e..8518fcd050 100644 --- a/app/definitions/rest/v1/groups.ts +++ b/app/definitions/rest/v1/groups.ts @@ -1,10 +1,10 @@ -import type { IMessage } from '../../IMessage'; +import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IRoom } from '../../IRoom'; import type { IUser } from '../../IUser'; export type GroupsEndpoints = { 'groups.files': { - GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => { + GET: (params: { roomId: IRoom['_id']; count: number; sort: string | { uploadedAt: number }; query: string }) => { files: IMessage[]; total: number; }; @@ -17,4 +17,9 @@ export type GroupsEndpoints = { total: number; }; }; + 'groups.history': { + GET: (params: { roomId: string; count: number; latest?: string }) => { + messages: IMessageFromServer[]; + }; + }; }; diff --git a/app/definitions/rest/v1/im.ts b/app/definitions/rest/v1/im.ts index a9502ab2d8..b2927b03c3 100644 --- a/app/definitions/rest/v1/im.ts +++ b/app/definitions/rest/v1/im.ts @@ -1,4 +1,4 @@ -import type { IMessage } from '../../IMessage'; +import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IRoom } from '../../IRoom'; import type { IUser } from '../../IUser'; @@ -20,7 +20,7 @@ export type ImEndpoints = { }; }; 'im.files': { - GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => { + GET: (params: { roomId: IRoom['_id']; count: number; sort: string | { uploadedAt: number }; query: string }) => { files: IMessage[]; total: number; }; @@ -33,4 +33,9 @@ export type ImEndpoints = { total: number; }; }; + 'im.history': { + GET: (params: { roomId: string; count: number; latest?: string }) => { + messages: IMessageFromServer[]; + }; + }; }; diff --git a/app/definitions/rest/v1/omnichannel.ts b/app/definitions/rest/v1/omnichannel.ts index 784415a534..df44413706 100644 --- a/app/definitions/rest/v1/omnichannel.ts +++ b/app/definitions/rest/v1/omnichannel.ts @@ -179,7 +179,7 @@ export type OmnichannelEndpoints = { departmentId?: ILivechatAgent['_id']; offset: number; count: number; - sort: string; + sort: string | { uploadedAt: number }; }) => { queue: { chats: number; diff --git a/app/lib/encryption/encryption.ts b/app/lib/encryption/encryption.ts index be979e278a..d5d87d96f4 100644 --- a/app/lib/encryption/encryption.ts +++ b/app/lib/encryption/encryption.ts @@ -244,7 +244,7 @@ class Encryption { const threadMessagesToDecrypt = await threadMessagesCollection.query(...whereClause).fetch(); // Concat messages/threads/threadMessages - let toDecrypt: (TThreadModel | TThreadMessageModel)[] = [ + let toDecrypt: (TThreadModel | TThreadMessageModel | TMessageModel)[] = [ ...messagesToDecrypt, ...threadsToDecrypt, ...threadMessagesToDecrypt @@ -259,7 +259,7 @@ class Encryption { newMessage = await this.decryptMessage({ t, rid, - msg, + msg: msg as string, tmsg }); } @@ -464,12 +464,13 @@ class Encryption { } const { rid } = message; - const roomE2E = await this.getRoomInstance(rid as string); + const roomE2E = await this.getRoomInstance(rid); return roomE2E.decrypt(message); }; // Decrypt multiple messages - decryptMessages = (messages: IMessage[]) => Promise.all(messages.map((m: IMessage) => this.decryptMessage(m))); + decryptMessages = (messages: Partial[]) => + Promise.all(messages.map((m: Partial) => this.decryptMessage(m as IMessage))); // Decrypt multiple subscriptions decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s))); diff --git a/app/lib/methods/getThreadName.ts b/app/lib/methods/getThreadName.ts index b6ed94580a..3f4c428827 100644 --- a/app/lib/methods/getThreadName.ts +++ b/app/lib/methods/getThreadName.ts @@ -6,9 +6,9 @@ import { getThreadById } from '../database/services/Thread'; import log from '../../utils/log'; import { Encryption } from '../encryption'; import getSingleMessage from './getSingleMessage'; -import { IThread, TThreadModel } from '../../definitions'; +import { IMessage, IThread, TThreadModel } from '../../definitions'; -const buildThreadName = (thread: IThread): string | undefined => thread.msg || thread?.attachments?.[0]?.title; +const buildThreadName = (thread: IThread | IMessage): string | undefined => thread.msg || thread?.attachments?.[0]?.title; const getThreadName = async (rid: string, tmid: string, messageId: string): Promise => { let tmsg: string | undefined; diff --git a/app/lib/methods/helpers/buildMessage.ts b/app/lib/methods/helpers/buildMessage.ts index 44c30ffa2d..b17574c973 100644 --- a/app/lib/methods/helpers/buildMessage.ts +++ b/app/lib/methods/helpers/buildMessage.ts @@ -1,8 +1,8 @@ -import { IMessage } from '../../../definitions'; +import { IMessage, IThreadResult } from '../../../definitions'; import messagesStatus from '../../../constants/messagesStatus'; import normalizeMessage from './normalizeMessage'; -export default (message: IMessage): IMessage => { +export default (message: Partial | IThreadResult): Partial | IThreadResult => { message.status = messagesStatus.SENT; return normalizeMessage(message); }; diff --git a/app/lib/methods/loadMessagesForRoom.ts b/app/lib/methods/loadMessagesForRoom.ts index db64a05caa..6eef4fa0c8 100644 --- a/app/lib/methods/loadMessagesForRoom.ts +++ b/app/lib/methods/loadMessagesForRoom.ts @@ -1,13 +1,12 @@ import moment from 'moment'; -import { MESSAGE_TYPE_LOAD_MORE } from '../../constants/messageTypeLoad'; +import { IMessage, MessageType, TMessageModel } from '../../definitions'; import log from '../../utils/log'; import { getMessageById } from '../database/services/Message'; +import roomTypeToApiType, { RoomTypes } from '../rocketchat/methods/roomTypeToApiType'; +import sdk from '../rocketchat/services/sdk'; import { generateLoadMoreId } from '../utils'; import updateMessages from './updateMessages'; -import { IMessage, TMessageModel } from '../../definitions'; -import sdk from '../rocketchat/services/sdk'; -import roomTypeToApiType, { RoomTypes } from '../rocketchat/methods/roomTypeToApiType'; const COUNT = 50; @@ -23,9 +22,8 @@ async function load({ rid: roomId, latest, t }: { rid: string; latest?: string; } // RC 0.48.0 - // @ts-ignore - const data: any = await sdk.get(`${apiType}.history`, params); - if (!data || data.status === 'error') { + const data = await sdk.get(`${apiType}.history`, params); + if (!data.success) { return []; } return data.messages; @@ -36,22 +34,22 @@ export default function loadMessagesForRoom(args: { t: RoomTypes; latest: string; loaderItem: TMessageModel; -}): Promise { +}): Promise[]> { return new Promise(async (resolve, reject) => { try { - const data = await load(args); + const data: Partial[] = await load(args); if (data?.length) { const lastMessage = data[data.length - 1]; - const lastMessageRecord = await getMessageById(lastMessage._id); + const lastMessageRecord = await getMessageById(lastMessage._id as string); if (!lastMessageRecord && data.length === COUNT) { - const loadMoreItem = { - _id: generateLoadMoreId(lastMessage._id), + const loadMoreMessage = { + _id: generateLoadMoreId(lastMessage._id as string), rid: lastMessage.rid, - ts: moment(lastMessage.ts).subtract(1, 'millisecond'), - t: MESSAGE_TYPE_LOAD_MORE, + ts: moment(lastMessage.ts).subtract(1, 'millisecond').toString(), + t: 'load_more' as MessageType, msg: lastMessage.msg }; - data.push(loadMoreItem); + data.push(loadMoreMessage); } await updateMessages({ rid: args.rid, update: data, loaderItem: args.loaderItem }); return resolve(data); diff --git a/app/lib/methods/updateMessages.ts b/app/lib/methods/updateMessages.ts index 1e86d79972..6d4115ae9f 100644 --- a/app/lib/methods/updateMessages.ts +++ b/app/lib/methods/updateMessages.ts @@ -12,8 +12,8 @@ import { getSubscriptionByRoomId } from '../database/services/Subscription'; interface IUpdateMessages { rid: string; - update: IMessage[]; - remove?: IMessage[]; + update: Partial[]; + remove?: Partial[]; loaderItem?: TMessageModel; } @@ -37,7 +37,7 @@ export default async function updateMessages({ // Decrypt these messages update = await Encryption.decryptMessages(update); - const messagesIds: string[] = [...update.map(m => m._id), ...remove.map(m => m._id)]; + const messagesIds: string[] = [...update.map(m => m._id as string), ...remove.map(m => m._id as string)]; const msgCollection = db.get('messages'); const threadCollection = db.get('threads'); const threadMessagesCollection = db.get('thread_messages'); @@ -49,11 +49,11 @@ export default async function updateMessages({ .query(Q.where('subscription_id', rid), Q.where('id', Q.oneOf(messagesIds))) .fetch(); - update = update.map(m => buildMessage(m)); + update = update.map(m => buildMessage(m)) as IMessage[]; // filter loaders to delete let loadersToDelete: TMessageModel[] = allMessagesRecords.filter(i1 => - update.find(i2 => i1.id === generateLoadMoreId(i2._id)) + update.find(i2 => i1.id === generateLoadMoreId(i2._id as string)) ); // Delete diff --git a/app/lib/rocketchat/methods/roomTypeToApiType.ts b/app/lib/rocketchat/methods/roomTypeToApiType.ts index ecc0d16c24..b66b78d2ce 100644 --- a/app/lib/rocketchat/methods/roomTypeToApiType.ts +++ b/app/lib/rocketchat/methods/roomTypeToApiType.ts @@ -1,8 +1,14 @@ -const types = { - c: 'channels', - d: 'im', - p: 'groups', - l: 'channels' +enum ETypes { + Channels = 'channels', + Im = 'im', + Groups = 'groups' +} + +export const types = { + c: ETypes.Channels, + d: ETypes.Im, + p: ETypes.Groups, + l: ETypes.Channels }; // TODO: refactor this diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 5ceec17af9..d2ea4527de 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -287,7 +287,7 @@ class ThreadMessagesView extends React.Component buildMessage(m)); + update = update.map(m => buildMessage(m)) as IThreadResult[]; // filter threads threadsToCreate = update.filter(i1 => !allThreadsRecords.find((i2: { id: string }) => i1._id === i2.id)); threadsToUpdate = allThreadsRecords.filter((i1: { id: string }) => update.find(i2 => i1.id === i2._id));