Compare commits

...

12 Commits

15 changed files with 209 additions and 85 deletions

View File

@ -1,3 +1,5 @@
import { IUser } from './IUser';
export interface IAttachment { export interface IAttachment {
ts: string | Date; ts: string | Date;
title: string; title: string;
@ -23,3 +25,30 @@ export interface IAttachment {
color?: string; color?: string;
thumb_url?: string; thumb_url?: string;
} }
export interface IServerAttachment {
_id: string;
name: string;
size: number;
type: string;
rid: string;
userId: string;
AmazonS3: { path: string };
store: string;
identify: {
format: string;
size: {
width: number;
height: number;
};
};
complete: boolean;
etag: string;
path: string;
progress: boolean;
token: string;
uploadedAt: string | Date;
uploading: boolean;
url: string;
user: Pick<IUser, '_id' | 'username' | 'name'>;
}

View File

@ -87,6 +87,18 @@ export interface IMessageFromServer {
drid?: string; drid?: string;
dcount?: number; dcount?: number;
dml: string | Date; dml: string | Date;
starred?:
| {
_id: string;
}
| boolean;
pinned?: boolean;
pinnedAt?: string | Date;
pinnedBy?: {
_id: string;
username: string;
};
score?: number;
} }
export interface ILoadMoreMessage { export interface ILoadMoreMessage {
@ -106,7 +118,11 @@ export interface IMessage extends IMessageFromServer {
emoji?: string; emoji?: string;
status?: number; status?: number;
pinned?: boolean; pinned?: boolean;
starred?: boolean; starred?:
| {
_id: string;
}
| boolean;
editedBy?: IEditedBy; editedBy?: IEditedBy;
reactions?: IReaction[]; reactions?: IReaction[];
role?: string; role?: string;

View File

@ -57,6 +57,14 @@ export interface IAvatar {
service?: any; service?: any;
} }
export interface IAvatarSuggestion {
[service: string]: {
url: string;
blob: string;
contentType: string;
};
}
export interface IProfileViewState { export interface IProfileViewState {
saving: boolean; saving: boolean;
name: string; name: string;
@ -66,13 +74,7 @@ export interface IProfileViewState {
currentPassword: string | null; currentPassword: string | null;
avatarUrl: string | null; avatarUrl: string | null;
avatar: IAvatar; avatar: IAvatar;
avatarSuggestions: { avatarSuggestions: IAvatarSuggestion;
[service: string]: {
url: string;
blob: string;
contentType: string;
};
};
customFields: { customFields: {
[key: string | number]: string; [key: string | number]: string;
}; };

View File

@ -1,18 +1,15 @@
import { ITeam } from '../../ITeam'; import { ITeam } from '../../ITeam';
import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IMessageFromServer } from '../../IMessage';
import type { IServerRoom } from '../../IRoom'; import type { IServerRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
import { IServerAttachment } from '../../IAttachment';
export type ChannelsEndpoints = { export type ChannelsEndpoints = {
'channels.files': { 'channels.files': {
GET: (params: { GET: (params: { roomId: IServerRoom['_id']; offset: number; sort: string | { uploadedAt: number } }) => {
roomId: IServerRoom['_id']; files: IServerAttachment[];
offset: number;
count: number; count: number;
sort: string | { uploadedAt: number }; offset: number;
query: string;
}) => {
files: IMessage[];
total: number; total: number;
}; };
}; };
@ -99,4 +96,14 @@ export type ChannelsEndpoints = {
'channels.removeLeader': { 'channels.removeLeader': {
POST: (params: { roomId: string; userId: string }) => {}; POST: (params: { roomId: string; userId: string }) => {};
}; };
'channels.messages': {
GET: (params: {
roomId: IServerRoom['_id'];
query: { 'mentions._id': { $in: string[] } } | { 'starred._id': { $in: string[] } } | { pinned: boolean };
offset: number;
sort: { ts: number };
}) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -60,4 +60,9 @@ export type ChatEndpoints = {
'chat.ignoreUser': { 'chat.ignoreUser': {
GET: (params: { rid: string; userId: string; ignore: boolean }) => {}; GET: (params: { rid: string; userId: string; ignore: boolean }) => {};
}; };
'chat.search': {
GET: (params: { roomId: IServerRoom['_id']; searchText: string; count: number; offset: number }) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -1,12 +1,15 @@
import { ITeam } from '../../ITeam'; import { ITeam } from '../../ITeam';
import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IMessageFromServer } from '../../IMessage';
import type { IServerRoom } from '../../IRoom'; import type { IServerRoom } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
import { IServerAttachment } from '../../IAttachment';
export type GroupsEndpoints = { export type GroupsEndpoints = {
'groups.files': { 'groups.files': {
GET: (params: { roomId: IServerRoom['_id']; count: number; sort: string | { uploadedAt: number }; query: string }) => { GET: (params: { roomId: IServerRoom['_id']; offset: number; sort: string | { uploadedAt: number } }) => {
files: IMessage[]; files: IServerAttachment[];
count: number;
offset: number;
total: number; total: number;
}; };
}; };
@ -69,4 +72,14 @@ export type GroupsEndpoints = {
'groups.leave': { 'groups.leave': {
POST: (params: { roomId: string }) => {}; POST: (params: { roomId: string }) => {};
}; };
'groups.messages': {
GET: (params: {
roomId: IServerRoom['_id'];
query: { 'mentions._id': { $in: string[] } } | { 'starred._id': { $in: string[] } } | { pinned: boolean };
offset: number;
sort: { ts: number };
}) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -1,6 +1,7 @@
import type { IMessage, IMessageFromServer } from '../../IMessage'; import type { IMessageFromServer } from '../../IMessage';
import type { IServerRoom, RoomID, RoomType } from '../../IRoom'; import type { IServerRoom, RoomID, RoomType } from '../../IRoom';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
import { IServerAttachment } from '../../IAttachment';
export type ImEndpoints = { export type ImEndpoints = {
'im.create': { 'im.create': {
@ -25,8 +26,10 @@ export type ImEndpoints = {
}; };
}; };
'im.files': { 'im.files': {
GET: (params: { roomId: IServerRoom['_id']; count: number; sort: string | { uploadedAt: number }; query: string }) => { GET: (params: { roomId: IServerRoom['_id']; offset: number; sort: string | { uploadedAt: number } }) => {
files: IMessage[]; files: IServerAttachment[];
count: number;
offset: number;
total: number; total: number;
}; };
}; };
@ -55,4 +58,14 @@ export type ImEndpoints = {
'im.leave': { 'im.leave': {
POST: (params: { roomId: string }) => {}; POST: (params: { roomId: string }) => {};
}; };
'im.messages': {
GET: (params: {
roomId: IServerRoom['_id'];
query: { 'mentions._id': { $in: string[] } } | { 'starred._id': { $in: string[] } } | { pinned: boolean };
offset: number;
sort: { ts: number };
}) => {
messages: IMessageFromServer[];
};
};
}; };

View File

@ -43,4 +43,16 @@ export type UsersEndpoints = {
user: IUser; user: IUser;
}; };
}; };
'users.getUsernameSuggestion': {
GET: () => { result: string };
};
'users.resetAvatar': {
POST: (params: { userId: string }) => {};
};
'users.getPreferences': {
GET: (params: { userId: IUser['_id'] }) => {
preferences: INotificationPreferences;
success: boolean;
};
};
}; };

View File

@ -103,9 +103,16 @@ export default class RoomSubscription {
} }
}; };
handleConnection = () => { handleConnection = async () => {
try {
reduxStore.dispatch(clearUserTyping()); reduxStore.dispatch(clearUserTyping());
RocketChat.loadMissedMessages({ rid: this.rid }).catch(e => console.log(e)); await RocketChat.loadMissedMessages({ rid: this.rid });
const _lastOpen = new Date();
this.read(_lastOpen);
this.lastOpen = _lastOpen;
} catch (e) {
log(e);
}
}; };
handleNotifyRoomReceived = protectedFunction((ddpMessage: IDDPMessage) => { handleNotifyRoomReceived = protectedFunction((ddpMessage: IDDPMessage) => {

View File

@ -3,7 +3,7 @@ import { TEAM_TYPE } from '../../../definitions/ITeam';
import roomTypeToApiType, { RoomTypes } from '../methods/roomTypeToApiType'; import roomTypeToApiType, { RoomTypes } from '../methods/roomTypeToApiType';
import { SubscriptionType, INotificationPreferences, IRoomNotifications } from '../../../definitions'; import { SubscriptionType, INotificationPreferences, IRoomNotifications } from '../../../definitions';
import { ISpotlight } from '../../../definitions/ISpotlight'; import { ISpotlight } from '../../../definitions/ISpotlight';
import { IParams } from '../../../definitions/IProfileViewInterfaces'; import { IAvatarSuggestion, IParams } from '../../../definitions/IProfileViewInterfaces';
export const createChannel = ({ export const createChannel = ({
name, name,
@ -290,10 +290,8 @@ export const getChannelInfo = (roomId: string) =>
// RC 0.48.0 // RC 0.48.0
sdk.get('channels.info', { roomId }); sdk.get('channels.info', { roomId });
export const getUserPreferences = (userId: string): any => export const getUserPreferences = (userId: string) =>
// RC 0.62.0 // RC 0.62.0
// TODO: missing definitions from server
// @ts-ignore
sdk.get('users.getPreferences', { userId }); sdk.get('users.getPreferences', { userId });
export const getRoomInfo = (roomId: string) => export const getRoomInfo = (roomId: string) =>
@ -568,14 +566,12 @@ export const getRoomRoles = (roomId: string, type: SubscriptionType): any =>
// @ts-ignore // @ts-ignore
sdk.get(`${roomTypeToApiType(type)}.roles`, { roomId }); sdk.get(`${roomTypeToApiType(type)}.roles`, { roomId });
export const getAvatarSuggestion = () => export const getAvatarSuggestion = (): Promise<IAvatarSuggestion> =>
// RC 0.51.0 // RC 0.51.0
sdk.methodCallWrapper('getAvatarSuggestion'); sdk.methodCallWrapper('getAvatarSuggestion');
export const resetAvatar = (userId: string): any => export const resetAvatar = (userId: string) =>
// RC 0.55.0 // RC 0.55.0
// TODO: missing definitions from server
// @ts-ignore
sdk.post('users.resetAvatar', { userId }); sdk.post('users.resetAvatar', { userId });
export const setAvatarFromService = ({ export const setAvatarFromService = ({
@ -586,36 +582,39 @@ export const setAvatarFromService = ({
data: any; data: any;
contentType?: string; contentType?: string;
service?: string | null; service?: string | null;
}) => }): Promise<void> =>
// RC 0.51.0 // RC 0.51.0
sdk.methodCallWrapper('setAvatarFromService', data, contentType, service); sdk.methodCallWrapper('setAvatarFromService', data, contentType, service);
export const getUsernameSuggestion = (): any => export const getUsernameSuggestion = () =>
// RC 0.65.0 // RC 0.65.0
// TODO: missing definitions from server
// @ts-ignore
sdk.get('users.getUsernameSuggestion'); sdk.get('users.getUsernameSuggestion');
export const getFiles = (roomId: string, type: RoomTypes, offset: number): any => export const getFiles = (roomId: string, type: SubscriptionType, offset: number) => {
const t = type as SubscriptionType.DIRECT | SubscriptionType.CHANNEL | SubscriptionType.GROUP;
// RC 0.59.0 // RC 0.59.0
// TODO: missing definitions from server return sdk.get(`${roomTypeToApiType(t)}.files`, {
// @ts-ignore
sdk.get(`${roomTypeToApiType(type)}.files`, {
roomId, roomId,
offset, offset,
sort: { uploadedAt: -1 } sort: { uploadedAt: -1 }
}); });
};
export const getMessages = (roomId: string, type: RoomTypes, query: any, offset: number): any => export const getMessages = (
roomId: string,
type: SubscriptionType,
query: { 'mentions._id': { $in: string[] } } | { 'starred._id': { $in: string[] } } | { pinned: boolean },
offset: number
) => {
const t = type as SubscriptionType.DIRECT | SubscriptionType.CHANNEL | SubscriptionType.GROUP;
// RC 0.59.0 // RC 0.59.0
// TODO: missing definitions from server return sdk.get(`${roomTypeToApiType(t)}.messages`, {
// @ts-ignore
sdk.get(`${roomTypeToApiType(type)}.messages`, {
roomId, roomId,
query, query,
offset, offset,
sort: { ts: -1 } sort: { ts: -1 }
}); });
};
export const getReadReceipts = (messageId: string): any => export const getReadReceipts = (messageId: string): any =>
// RC 0.63.0 // RC 0.63.0
@ -625,10 +624,8 @@ export const getReadReceipts = (messageId: string): any =>
messageId messageId
}); });
export const searchMessages = (roomId: string, searchText: string, count: number, offset: number): any => export const searchMessages = (roomId: string, searchText: string, count: number, offset: number) =>
// RC 0.60.0 // RC 0.60.0
// TODO: missing definitions from server
// @ts-ignore
sdk.get('chat.search', { sdk.get('chat.search', {
roomId, roomId,
searchText, searchText,

View File

@ -9,8 +9,6 @@ import log from '../utils/log';
import mergeSubscriptionsRooms from '../lib/methods/helpers/mergeSubscriptionsRooms'; import mergeSubscriptionsRooms from '../lib/methods/helpers/mergeSubscriptionsRooms';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import buildMessage from '../lib/methods/helpers/buildMessage'; import buildMessage from '../lib/methods/helpers/buildMessage';
import protectedFunction from '../lib/methods/helpers/protectedFunction';
import UserPreferences from '../lib/userPreferences';
const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) { const updateRooms = function* updateRooms({ server, newRoomsUpdatedAt }) {
const serversDB = database.servers; const serversDB = database.servers;
@ -82,6 +80,7 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
}) })
), ),
...subsToUpdate.map(subscription => { ...subsToUpdate.map(subscription => {
try {
const newSub = subscriptions.find(s => s._id === subscription._id); const newSub = subscriptions.find(s => s._id === subscription._id);
return subscription.prepareUpdate(() => { return subscription.prepareUpdate(() => {
if (newSub.announcement) { if (newSub.announcement) {
@ -91,24 +90,36 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) {
} }
Object.assign(subscription, newSub); Object.assign(subscription, newSub);
}); });
} catch (e) {
log(e);
return null;
}
}),
...subsToDelete.map(subscription => {
try {
return subscription.prepareDestroyPermanently();
} catch (e) {
log(e);
return null;
}
}), }),
...subsToDelete.map(subscription => subscription.prepareDestroyPermanently()),
...messagesToCreate.map(message => ...messagesToCreate.map(message =>
messagesCollection.prepareCreate( messagesCollection.prepareCreate(m => {
protectedFunction(m => {
m._raw = sanitizedRaw({ id: message._id }, messagesCollection.schema); m._raw = sanitizedRaw({ id: message._id }, messagesCollection.schema);
m.subscription.id = message.rid; m.subscription.id = message.rid;
return Object.assign(m, message); return Object.assign(m, message);
}) })
)
), ),
...messagesToUpdate.map(message => { ...messagesToUpdate.map(message => {
const newMessage = lastMessages.find(m => m._id === message.id); const newMessage = lastMessages.find(m => m._id === message.id);
return message.prepareUpdate( return message.prepareUpdate(() => {
protectedFunction(() => { try {
Object.assign(message, newMessage); return Object.assign(message, newMessage);
}) } catch (e) {
); log(e);
return null;
}
});
}) })
]; ];

View File

@ -196,9 +196,10 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
name: I18n.t('Files'), name: I18n.t('Files'),
fetchFunc: async () => { fetchFunc: async () => {
const { messages } = this.state; const { messages } = this.state;
// @ts-ignore
const result = await RocketChat.getFiles(this.rid, this.t, messages.length); const result = await RocketChat.getFiles(this.rid, this.t, messages.length);
if (result.success) {
return { ...result, messages: result.files }; return { ...result, messages: result.files };
}
}, },
noDataMsg: I18n.t('No_files'), noDataMsg: I18n.t('No_files'),
testID: 'room-files-view', testID: 'room-files-view',
@ -226,7 +227,6 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
name: I18n.t('Mentions'), name: I18n.t('Mentions'),
fetchFunc: () => { fetchFunc: () => {
const { messages } = this.state; const { messages } = this.state;
// @ts-ignore
return RocketChat.getMessages(this.rid, this.t, { 'mentions._id': { $in: [user.id] } }, messages.length); return RocketChat.getMessages(this.rid, this.t, { 'mentions._id': { $in: [user.id] } }, messages.length);
}, },
noDataMsg: I18n.t('No_mentioned_messages'), noDataMsg: I18n.t('No_mentioned_messages'),
@ -239,7 +239,6 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
name: I18n.t('Starred'), name: I18n.t('Starred'),
fetchFunc: () => { fetchFunc: () => {
const { messages } = this.state; const { messages } = this.state;
// @ts-ignore
return RocketChat.getMessages(this.rid, this.t, { 'starred._id': { $in: [user.id] } }, messages.length); return RocketChat.getMessages(this.rid, this.t, { 'starred._id': { $in: [user.id] } }, messages.length);
}, },
noDataMsg: I18n.t('No_starred_messages'), noDataMsg: I18n.t('No_starred_messages'),
@ -260,7 +259,6 @@ class MessagesView extends React.Component<IMessagesViewProps, any> {
name: I18n.t('Pinned'), name: I18n.t('Pinned'),
fetchFunc: () => { fetchFunc: () => {
const { messages } = this.state; const { messages } = this.state;
// @ts-ignore
return RocketChat.getMessages(this.rid, this.t, { pinned: true }, messages.length); return RocketChat.getMessages(this.rid, this.t, { pinned: true }, messages.length);
}, },
noDataMsg: I18n.t('No_pinned_messages'), noDataMsg: I18n.t('No_pinned_messages'),

View File

@ -157,11 +157,13 @@ class SearchMessagesView extends React.Component<ISearchMessagesViewProps, ISear
if (result.success) { if (result.success) {
return result.messages; return result.messages;
} }
};
return [];
};
getMessages = async (searchText: string, debounced?: boolean) => { getMessages = async (searchText: string, debounced?: boolean) => {
try { try {
const messages = await this.searchMessages(searchText); const messages = await this.searchMessages(searchText);
// @ts-ignore TODO: find a way to deal with the difference between IMessageFromServer and TMessageModel expected by state
this.setState(prevState => ({ this.setState(prevState => ({
messages: debounced ? messages : [...prevState.messages, ...messages], messages: debounced ? messages : [...prevState.messages, ...messages],
loading: false loading: false

View File

@ -15,6 +15,7 @@ import { getUserSelector } from '../../selectors/login';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import { OPTIONS } from './options'; import { OPTIONS } from './options';
import { ProfileStackParamList } from '../../stacks/types'; import { ProfileStackParamList } from '../../stacks/types';
import { INotificationPreferences } from '../../definitions';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
pickerText: { pickerText: {
@ -26,11 +27,7 @@ const styles = StyleSheet.create({
type TKey = 'desktopNotifications' | 'pushNotifications' | 'emailNotificationMode'; type TKey = 'desktopNotifications' | 'pushNotifications' | 'emailNotificationMode';
interface IUserNotificationPreferencesViewState { interface IUserNotificationPreferencesViewState {
preferences: { preferences: INotificationPreferences;
desktopNotifications?: string;
pushNotifications?: string;
emailNotificationMode?: string;
};
loading: boolean; loading: boolean;
} }
@ -53,7 +50,7 @@ class UserNotificationPreferencesView extends React.Component<
constructor(props: IUserNotificationPreferencesViewProps) { constructor(props: IUserNotificationPreferencesViewProps) {
super(props); super(props);
this.state = { this.state = {
preferences: {}, preferences: {} as INotificationPreferences,
loading: false loading: false
}; };
} }
@ -62,9 +59,11 @@ class UserNotificationPreferencesView extends React.Component<
const { user } = this.props; const { user } = this.props;
const { id } = user; const { id } = user;
const result = await RocketChat.getUserPreferences(id); const result = await RocketChat.getUserPreferences(id);
if (result.success) {
const { preferences } = result; const { preferences } = result;
this.setState({ preferences, loading: true }); this.setState({ preferences, loading: true });
} }
}
findDefaultOption = (key: TKey) => { findDefaultOption = (key: TKey) => {
const { preferences } = this.state; const { preferences } = this.state;

View File

@ -0,0 +1,13 @@
diff --git a/node_modules/@rocket.chat/sdk/lib/drivers/ddp.ts b/node_modules/@rocket.chat/sdk/lib/drivers/ddp.ts
index e751551..02703a0 100644
--- a/node_modules/@rocket.chat/sdk/lib/drivers/ddp.ts
+++ b/node_modules/@rocket.chat/sdk/lib/drivers/ddp.ts
@@ -298,7 +298,7 @@ export class Socket extends EventEmitter {
login = async (credentials: any) => {
const params = this.loginParams(credentials)
this.resume = (await this.call('login', params) as ILoginResult)
- await this.subscribeAll()
+ this.subscribeAll().catch(console.log)
this.emit('login', this.resume)
return this.resume
}