Chore: Server API types POC - loadMessagesForRoom (#3765)

* create interface and implements base types

* fix some types

* Update app/lib/methods/updateMessages.ts

Co-authored-by: Diego Mello <diegolmello@gmail.com>

* fix date type

* apply types changes

* fix type

* fix date value

* fix types

* typescript things...

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gleidson Daniel Silva 2022-02-25 18:32:03 -03:00 committed by GitHub
parent 4224f4d9d0
commit 4172d563b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 126 additions and 61 deletions

View File

@ -3,7 +3,7 @@ import { MarkdownAST } from '@rocket.chat/message-parser';
import { IAttachment } from './IAttachment'; import { IAttachment } from './IAttachment';
import { IReaction } from './IReaction'; 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'; export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj';
@ -59,22 +59,61 @@ export interface ILastMessage {
status: boolean; 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; _id: string;
rid: string; rid: string;
msg?: 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;
ts: string;
t: string;
msg: string;
}
export interface IMessage extends IMessageFromServer {
id: string; id: string;
t?: MessageType; t?: MessageType;
ts: string | Date;
u: IUserMessage;
alias?: string; alias?: string;
parseUrls?: boolean; parseUrls?: boolean;
groupable?: boolean;
avatar?: string; avatar?: string;
emoji?: string; emoji?: string;
attachments?: IAttachment[];
urls?: IUrl[];
_updatedAt: string | Date;
status?: number; status?: number;
pinned?: boolean; pinned?: boolean;
starred?: boolean; starred?: boolean;
@ -88,8 +127,6 @@ export interface IMessage {
tcount?: number; tcount?: number;
tlm?: string | Date; tlm?: string | Date;
replies?: string[]; replies?: string[];
mentions?: IUserMention[];
channels?: IUserChannel[];
unread?: boolean; unread?: boolean;
autoTranslate?: boolean; autoTranslate?: boolean;
translations?: ITranslations[]; translations?: ITranslations[];
@ -97,8 +134,9 @@ export interface IMessage {
blocks?: any; blocks?: any;
e2e?: string; e2e?: string;
tshow?: boolean; tshow?: boolean;
md?: MarkdownAST;
subscription?: { id: string }; subscription?: { id: string };
} }
export type TMessageModel = IMessage & Model; export type TMessageModel = IMessage & Model;
export type TTypeMessages = IMessageFromServer | ILoadMoreMessage | IMessage;

View File

@ -30,6 +30,7 @@ export interface IThreadResult {
channels?: IUserChannel[]; channels?: IUserChannel[];
replies?: string[]; replies?: string[];
tcount?: number; tcount?: number;
status?: string;
tlm?: string | Date; tlm?: string | Date;
} }

View File

@ -1,11 +1,3 @@
export interface IUrl {
_id: number;
title: string;
description: string;
image: string;
url: string;
}
export interface IUrlFromServer { export interface IUrlFromServer {
url: string; url: string;
meta: { meta: {
@ -47,3 +39,11 @@ export interface IUrlFromServer {
}; };
ignoreParse: boolean; ignoreParse: boolean;
} }
export interface IUrl extends IUrlFromServer {
_id: number;
title: string;
description: string;
image: string;
url: string;
}

View File

@ -1,10 +1,16 @@
import type { IMessage } from '../../IMessage'; import type { IMessage, IMessageFromServer } from '../../IMessage';
import type { IRoom } from '../../IRoom'; import type { IRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
export type ChannelsEndpoints = { export type ChannelsEndpoints = {
'channels.files': { '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[]; files: IMessage[];
total: number; total: number;
}; };
@ -17,4 +23,9 @@ export type ChannelsEndpoints = {
total: number; total: number;
}; };
}; };
'channels.history': {
GET: (params: { roomId: string; count: number; latest?: string }) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -1,10 +1,10 @@
import type { IMessage } from '../../IMessage'; import type { IMessage, IMessageFromServer } from '../../IMessage';
import type { IRoom } from '../../IRoom'; import type { IRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
export type GroupsEndpoints = { export type GroupsEndpoints = {
'groups.files': { '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[]; files: IMessage[];
total: number; total: number;
}; };
@ -17,4 +17,9 @@ export type GroupsEndpoints = {
total: number; total: number;
}; };
}; };
'groups.history': {
GET: (params: { roomId: string; count: number; latest?: string }) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -1,4 +1,4 @@
import type { IMessage } from '../../IMessage'; import type { IMessage, IMessageFromServer } from '../../IMessage';
import type { IRoom } from '../../IRoom'; import type { IRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
@ -20,7 +20,7 @@ export type ImEndpoints = {
}; };
}; };
'im.files': { '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[]; files: IMessage[];
total: number; total: number;
}; };
@ -33,4 +33,9 @@ export type ImEndpoints = {
total: number; total: number;
}; };
}; };
'im.history': {
GET: (params: { roomId: string; count: number; latest?: string }) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -179,7 +179,7 @@ export type OmnichannelEndpoints = {
departmentId?: ILivechatAgent['_id']; departmentId?: ILivechatAgent['_id'];
offset: number; offset: number;
count: number; count: number;
sort: string; sort: string | { uploadedAt: number };
}) => { }) => {
queue: { queue: {
chats: number; chats: number;

View File

@ -244,7 +244,7 @@ class Encryption {
const threadMessagesToDecrypt = await threadMessagesCollection.query(...whereClause).fetch(); const threadMessagesToDecrypt = await threadMessagesCollection.query(...whereClause).fetch();
// Concat messages/threads/threadMessages // Concat messages/threads/threadMessages
let toDecrypt: (TThreadModel | TThreadMessageModel)[] = [ let toDecrypt: (TThreadModel | TThreadMessageModel | TMessageModel)[] = [
...messagesToDecrypt, ...messagesToDecrypt,
...threadsToDecrypt, ...threadsToDecrypt,
...threadMessagesToDecrypt ...threadMessagesToDecrypt
@ -259,7 +259,7 @@ class Encryption {
newMessage = await this.decryptMessage({ newMessage = await this.decryptMessage({
t, t,
rid, rid,
msg, msg: msg as string,
tmsg tmsg
}); });
} }
@ -464,12 +464,13 @@ class Encryption {
} }
const { rid } = message; const { rid } = message;
const roomE2E = await this.getRoomInstance(rid as string); const roomE2E = await this.getRoomInstance(rid);
return roomE2E.decrypt(message); return roomE2E.decrypt(message);
}; };
// Decrypt multiple messages // Decrypt multiple messages
decryptMessages = (messages: IMessage[]) => Promise.all(messages.map((m: IMessage) => this.decryptMessage(m))); decryptMessages = (messages: Partial<IMessage>[]) =>
Promise.all(messages.map((m: Partial<IMessage>) => this.decryptMessage(m as IMessage)));
// Decrypt multiple subscriptions // Decrypt multiple subscriptions
decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s))); decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s)));

View File

@ -6,9 +6,9 @@ import { getThreadById } from '../database/services/Thread';
import log from '../../utils/log'; import log from '../../utils/log';
import { Encryption } from '../encryption'; import { Encryption } from '../encryption';
import getSingleMessage from './getSingleMessage'; 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<string | undefined> => { const getThreadName = async (rid: string, tmid: string, messageId: string): Promise<string | undefined> => {
let tmsg: string | undefined; let tmsg: string | undefined;

View File

@ -1,8 +1,8 @@
import { IMessage } from '../../../definitions'; import { IMessage, IThreadResult } from '../../../definitions';
import messagesStatus from '../../../constants/messagesStatus'; import messagesStatus from '../../../constants/messagesStatus';
import normalizeMessage from './normalizeMessage'; import normalizeMessage from './normalizeMessage';
export default (message: IMessage): IMessage => { export default (message: Partial<IMessage> | IThreadResult): Partial<IMessage> | IThreadResult => {
message.status = messagesStatus.SENT; message.status = messagesStatus.SENT;
return normalizeMessage(message); return normalizeMessage(message);
}; };

View File

@ -1,13 +1,12 @@
import moment from 'moment'; import moment from 'moment';
import { MESSAGE_TYPE_LOAD_MORE } from '../../constants/messageTypeLoad'; import { IMessage, MessageType, TMessageModel } from '../../definitions';
import log from '../../utils/log'; import log from '../../utils/log';
import { getMessageById } from '../database/services/Message'; import { getMessageById } from '../database/services/Message';
import roomTypeToApiType, { RoomTypes } from '../rocketchat/methods/roomTypeToApiType';
import sdk from '../rocketchat/services/sdk';
import { generateLoadMoreId } from '../utils'; import { generateLoadMoreId } from '../utils';
import updateMessages from './updateMessages'; 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; const COUNT = 50;
@ -23,9 +22,8 @@ async function load({ rid: roomId, latest, t }: { rid: string; latest?: string;
} }
// RC 0.48.0 // RC 0.48.0
// @ts-ignore const data = await sdk.get(`${apiType}.history`, params);
const data: any = await sdk.get(`${apiType}.history`, params); if (!data.success) {
if (!data || data.status === 'error') {
return []; return [];
} }
return data.messages; return data.messages;
@ -36,22 +34,22 @@ export default function loadMessagesForRoom(args: {
t: RoomTypes; t: RoomTypes;
latest: string; latest: string;
loaderItem: TMessageModel; loaderItem: TMessageModel;
}): Promise<IMessage[] | []> { }): Promise<Partial<IMessage>[]> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const data = await load(args); const data: Partial<IMessage>[] = await load(args);
if (data?.length) { if (data?.length) {
const lastMessage = data[data.length - 1]; 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) { if (!lastMessageRecord && data.length === COUNT) {
const loadMoreItem = { const loadMoreMessage = {
_id: generateLoadMoreId(lastMessage._id), _id: generateLoadMoreId(lastMessage._id as string),
rid: lastMessage.rid, rid: lastMessage.rid,
ts: moment(lastMessage.ts).subtract(1, 'millisecond'), ts: moment(lastMessage.ts).subtract(1, 'millisecond').toString(),
t: MESSAGE_TYPE_LOAD_MORE, t: 'load_more' as MessageType,
msg: lastMessage.msg msg: lastMessage.msg
}; };
data.push(loadMoreItem); data.push(loadMoreMessage);
} }
await updateMessages({ rid: args.rid, update: data, loaderItem: args.loaderItem }); await updateMessages({ rid: args.rid, update: data, loaderItem: args.loaderItem });
return resolve(data); return resolve(data);

View File

@ -12,8 +12,8 @@ import { getSubscriptionByRoomId } from '../database/services/Subscription';
interface IUpdateMessages { interface IUpdateMessages {
rid: string; rid: string;
update: IMessage[]; update: Partial<IMessage>[];
remove?: IMessage[]; remove?: Partial<IMessage>[];
loaderItem?: TMessageModel; loaderItem?: TMessageModel;
} }
@ -37,7 +37,7 @@ export default async function updateMessages({
// Decrypt these messages // Decrypt these messages
update = await Encryption.decryptMessages(update); 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 msgCollection = db.get('messages');
const threadCollection = db.get('threads'); const threadCollection = db.get('threads');
const threadMessagesCollection = db.get('thread_messages'); 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))) .query(Q.where('subscription_id', rid), Q.where('id', Q.oneOf(messagesIds)))
.fetch(); .fetch();
update = update.map(m => buildMessage(m)); update = update.map(m => buildMessage(m)) as IMessage[];
// filter loaders to delete // filter loaders to delete
let loadersToDelete: TMessageModel[] = allMessagesRecords.filter(i1 => 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 // Delete

View File

@ -1,8 +1,14 @@
const types = { enum ETypes {
c: 'channels', Channels = 'channels',
d: 'im', Im = 'im',
p: 'groups', Groups = 'groups'
l: 'channels' }
export const types = {
c: ETypes.Channels,
d: ETypes.Im,
p: ETypes.Groups,
l: ETypes.Channels
}; };
// TODO: refactor this // TODO: refactor this

View File

@ -287,7 +287,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
} }
if (update && update.length) { if (update && update.length) {
update = update.map(m => buildMessage(m)); update = update.map(m => buildMessage(m)) as IThreadResult[];
// filter threads // filter threads
threadsToCreate = update.filter(i1 => !allThreadsRecords.find((i2: { id: string }) => i1._id === i2.id)); 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)); threadsToUpdate = allThreadsRecords.filter((i1: { id: string }) => update.find(i2 => i1.id === i2._id));