diff --git a/app/actions/login.js b/app/actions/login.js deleted file mode 100644 index e0f4c1e28..000000000 --- a/app/actions/login.js +++ /dev/null @@ -1,59 +0,0 @@ -import * as types from './actionsTypes'; - -export function loginRequest(credentials, logoutOnError, isFromWebView) { - return { - type: types.LOGIN.REQUEST, - credentials, - logoutOnError, - isFromWebView - }; -} - -export function loginSuccess(user) { - return { - type: types.LOGIN.SUCCESS, - user - }; -} - -export function loginFailure(err) { - return { - type: types.LOGIN.FAILURE, - err - }; -} - -export function logout(forcedByServer = false) { - return { - type: types.LOGOUT, - forcedByServer - }; -} - -export function setUser(user) { - return { - type: types.USER.SET, - user - }; -} - -export function setLoginServices(data) { - return { - type: types.LOGIN.SET_SERVICES, - data - }; -} - -export function setPreference(preference) { - return { - type: types.LOGIN.SET_PREFERENCE, - preference - }; -} - -export function setLocalAuthenticated(isLocalAuthenticated) { - return { - type: types.LOGIN.SET_LOCAL_AUTHENTICATED, - isLocalAuthenticated - }; -} diff --git a/app/actions/login.ts b/app/actions/login.ts new file mode 100644 index 000000000..dead31433 --- /dev/null +++ b/app/actions/login.ts @@ -0,0 +1,115 @@ +import { Action } from 'redux'; + +import { IUser } from '../definitions'; +import * as types from './actionsTypes'; + +interface ICredentials { + resume: string; + user: string; + password: string; +} + +interface ILoginRequest extends Action { + credentials: any; + logoutOnError?: boolean; + isFromWebView?: boolean; +} + +interface ILoginSuccess extends Action { + user: Partial; +} + +interface ILoginFailure extends Action { + err: Partial; +} + +interface ILogout extends Action { + forcedByServer: boolean; +} + +interface ISetUser extends Action { + user: Partial; +} + +interface ISetServices extends Action { + data: Record; +} + +interface ISetPreference extends Action { + preference: Record; +} + +interface ISetLocalAuthenticated extends Action { + isLocalAuthenticated: boolean; +} + +export type TActionsLogin = ILoginRequest & + ILoginSuccess & + ILoginFailure & + ILogout & + ISetUser & + ISetServices & + ISetPreference & + ISetLocalAuthenticated; + +export function loginRequest( + credentials: Partial, + logoutOnError?: boolean, + isFromWebView?: boolean +): ILoginRequest { + return { + type: types.LOGIN.REQUEST, + credentials, + logoutOnError, + isFromWebView + }; +} + +export function loginSuccess(user: Partial): ILoginSuccess { + return { + type: types.LOGIN.SUCCESS, + user + }; +} + +export function loginFailure(err: Record): ILoginFailure { + return { + type: types.LOGIN.FAILURE, + err + }; +} + +export function logout(forcedByServer = false): ILogout { + return { + type: types.LOGOUT, + forcedByServer + }; +} + +export function setUser(user: Partial): ISetUser { + return { + type: types.USER.SET, + user + }; +} + +export function setLoginServices(data: Record): ISetServices { + return { + type: types.LOGIN.SET_SERVICES, + data + }; +} + +export function setPreference(preference: Record): ISetPreference { + return { + type: types.LOGIN.SET_PREFERENCE, + preference + }; +} + +export function setLocalAuthenticated(isLocalAuthenticated: boolean): ISetLocalAuthenticated { + return { + type: types.LOGIN.SET_LOCAL_AUTHENTICATED, + isLocalAuthenticated + }; +} diff --git a/app/actions/room.js b/app/actions/room.js deleted file mode 100644 index 2753d7b9a..000000000 --- a/app/actions/room.js +++ /dev/null @@ -1,62 +0,0 @@ -import * as types from './actionsTypes'; - -export function subscribeRoom(rid) { - return { - type: types.ROOM.SUBSCRIBE, - rid - }; -} - -export function unsubscribeRoom(rid) { - return { - type: types.ROOM.UNSUBSCRIBE, - rid - }; -} - -export function leaveRoom(roomType, room, selected) { - return { - type: types.ROOM.LEAVE, - room, - roomType, - selected - }; -} - -export function deleteRoom(roomType, room, selected) { - return { - type: types.ROOM.DELETE, - room, - roomType, - selected - }; -} - -export function closeRoom(rid) { - return { - type: types.ROOM.CLOSE, - rid - }; -} - -export function forwardRoom(rid, transferData) { - return { - type: types.ROOM.FORWARD, - transferData, - rid - }; -} - -export function removedRoom() { - return { - type: types.ROOM.REMOVED - }; -} - -export function userTyping(rid, status = true) { - return { - type: types.ROOM.USER_TYPING, - rid, - status - }; -} diff --git a/app/actions/room.ts b/app/actions/room.ts new file mode 100644 index 000000000..9c817f565 --- /dev/null +++ b/app/actions/room.ts @@ -0,0 +1,109 @@ +import { Action } from 'redux'; + +import { ERoomType } from '../definitions/ERoomType'; +import { ROOM } from './actionsTypes'; + +// TYPE RETURN RELATED +type ISelected = Record; + +export interface ITransferData { + roomId: string; + userId?: string; + departmentId?: string; +} + +// ACTION RETURN RELATED +interface IBaseReturn extends Action { + rid: string; +} + +type TSubscribeRoom = IBaseReturn; +type TUnsubscribeRoom = IBaseReturn; +type TCloseRoom = IBaseReturn; + +type TRoom = Record; + +interface ILeaveRoom extends Action { + roomType: ERoomType; + room: TRoom; + selected?: ISelected; +} + +interface IDeleteRoom extends Action { + roomType: ERoomType; + room: TRoom; + selected?: ISelected; +} + +interface IForwardRoom extends Action { + transferData: ITransferData; + rid: string; +} + +interface IUserTyping extends Action { + rid: string; + status: boolean; +} + +export type TActionsRoom = TSubscribeRoom & TUnsubscribeRoom & TCloseRoom & ILeaveRoom & IDeleteRoom & IForwardRoom & IUserTyping; + +export function subscribeRoom(rid: string): TSubscribeRoom { + return { + type: ROOM.SUBSCRIBE, + rid + }; +} + +export function unsubscribeRoom(rid: string): TUnsubscribeRoom { + return { + type: ROOM.UNSUBSCRIBE, + rid + }; +} + +export function leaveRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): ILeaveRoom { + return { + type: ROOM.LEAVE, + room, + roomType, + selected + }; +} + +export function deleteRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): IDeleteRoom { + return { + type: ROOM.DELETE, + room, + roomType, + selected + }; +} + +export function closeRoom(rid: string): TCloseRoom { + return { + type: ROOM.CLOSE, + rid + }; +} + +export function forwardRoom(rid: string, transferData: ITransferData): IForwardRoom { + return { + type: ROOM.FORWARD, + transferData, + rid + }; +} + +export function removedRoom(): Action { + return { + type: ROOM.REMOVED + }; +} + +export function userTyping(rid: string, status = true): IUserTyping { + return { + type: ROOM.USER_TYPING, + rid, + status + }; +} diff --git a/app/containers/MessageBox/index.tsx b/app/containers/MessageBox/index.tsx index 68fd0ed2b..3c924df6f 100644 --- a/app/containers/MessageBox/index.tsx +++ b/app/containers/MessageBox/index.tsx @@ -82,6 +82,7 @@ interface IMessageBoxProps { isFocused(): boolean; user: { id: string; + _id: string; username: string; token: string; }; @@ -1184,5 +1185,5 @@ const mapStateToProps = (state: any) => ({ const dispatchToProps = { typing: (rid: any, status: any) => userTypingAction(rid, status) }; -// @ts-ignore + export default connect(mapStateToProps, dispatchToProps, null, { forwardRef: true })(withActionSheet(MessageBox)) as any; diff --git a/app/containers/message/utils.ts b/app/containers/message/utils.ts index 433f89de1..b10f6f741 100644 --- a/app/containers/message/utils.ts +++ b/app/containers/message/utils.ts @@ -45,7 +45,14 @@ export const SYSTEM_MESSAGES = [ 'message_snippeted', 'thread-created', 'room_e2e_enabled', - 'room_e2e_disabled' + 'room_e2e_disabled', + 'removed-user-from-team', + 'added-user-to-team', + 'user-added-room-to-team', + 'user-converted-to-team', + 'user-converted-to-channel', + 'user-deleted-room-from-team', + 'user-removed-room-from-team' ]; export const SYSTEM_MESSAGE_TYPES = { @@ -56,7 +63,14 @@ export const SYSTEM_MESSAGE_TYPES = { USER_JOINED_TEAM: 'ujt', USER_JOINED_DISCUSSION: 'ut', USER_LEFT_CHANNEL: 'ul', - USER_LEFT_TEAM: 'ult' + USER_LEFT_TEAM: 'ult', + REMOVED_USER_FROM_TEAM: 'removed-user-from-team', + ADDED_USER_TO_TEAM: 'added-user-to-team', + ADDED_ROOM_TO_TEAM: 'user-added-room-to-team', + CONVERTED_TO_TEAM: 'user-converted-to-team', + CONVERTED_TO_CHANNEL: 'user-converted-to-channel', + DELETED_ROOM_FROM_TEAM: 'user-deleted-room-from-team', + REMOVED_ROOM_FROM_TEAM: 'user-removed-room-from-team' }; export const SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME = [ @@ -67,7 +81,14 @@ export const SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME = [ SYSTEM_MESSAGE_TYPES.USER_JOINED_TEAM, SYSTEM_MESSAGE_TYPES.USER_JOINED_DISCUSSION, SYSTEM_MESSAGE_TYPES.USER_LEFT_CHANNEL, - SYSTEM_MESSAGE_TYPES.USER_LEFT_TEAM + SYSTEM_MESSAGE_TYPES.USER_LEFT_TEAM, + SYSTEM_MESSAGE_TYPES.REMOVED_USER_FROM_TEAM, + SYSTEM_MESSAGE_TYPES.ADDED_USER_TO_TEAM, + SYSTEM_MESSAGE_TYPES.ADDED_ROOM_TO_TEAM, + SYSTEM_MESSAGE_TYPES.CONVERTED_TO_TEAM, + SYSTEM_MESSAGE_TYPES.CONVERTED_TO_CHANNEL, + SYSTEM_MESSAGE_TYPES.DELETED_ROOM_FROM_TEAM, + SYSTEM_MESSAGE_TYPES.REMOVED_ROOM_FROM_TEAM ]; type TInfoMessage = { @@ -76,6 +97,7 @@ type TInfoMessage = { msg: string; author: { username: string }; }; + export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage): string => { const { username } = author; if (type === 'rm') { @@ -147,6 +169,27 @@ export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage): strin if (type === 'room_e2e_enabled') { return I18n.t('This_room_encryption_has_been_enabled_by__username_', { username }); } + if (type === 'removed-user-from-team') { + return I18n.t('Removed__username__from_team', { user_removed: username }); + } + if (type === 'added-user-to-team') { + return I18n.t('Added__username__to_team', { user_added: username }); + } + if (type === 'user-added-room-to-team') { + return I18n.t('added__roomName__to_team', { roomName: msg }); + } + if (type === 'user-converted-to-team') { + return I18n.t('Converted__roomName__to_team', { roomName: msg }); + } + if (type === 'user-converted-to-channel') { + return I18n.t('Converted__roomName__to_channel', { roomName: msg }); + } + if (type === 'user-deleted-room-from-team') { + return I18n.t('Deleted__roomName__', { roomName: msg }); + } + if (type === 'user-removed-room-from-team') { + return I18n.t('Removed__roomName__from_this_team', { roomName: msg }); + } return ''; }; diff --git a/app/definitions/ERoomType.ts b/app/definitions/ERoomType.ts new file mode 100644 index 000000000..010fa78f0 --- /dev/null +++ b/app/definitions/ERoomType.ts @@ -0,0 +1,7 @@ +export enum ERoomType { + p = 'group', + c = 'channel', + d = 'direct', + t = 'team', + l = 'omnichannel' +} diff --git a/app/definitions/IAttachment.ts b/app/definitions/IAttachment.ts index 4ff3aa8df..788d58763 100644 --- a/app/definitions/IAttachment.ts +++ b/app/definitions/IAttachment.ts @@ -1,5 +1,5 @@ export interface IAttachment { - ts: Date; + ts: string | Date; title: string; type: string; description: string; diff --git a/app/definitions/ICustomEmoji.ts b/app/definitions/ICustomEmoji.ts index 6a6c131a7..348e66098 100644 --- a/app/definitions/ICustomEmoji.ts +++ b/app/definitions/ICustomEmoji.ts @@ -1,8 +1,9 @@ import Model from '@nozbe/watermelondb/Model'; export interface ICustomEmoji { + _id: string; name?: string; - aliases?: string; + aliases?: string[]; extension: string; _updatedAt: Date; } diff --git a/app/definitions/IMessage.ts b/app/definitions/IMessage.ts index 15f046c8b..bc8f793da 100644 --- a/app/definitions/IMessage.ts +++ b/app/definitions/IMessage.ts @@ -63,7 +63,7 @@ export interface IMessage { _id: string; rid: string; msg?: string; - id?: string; + id: string; t?: MessageType; ts: string | Date; u: IUserMessage; @@ -74,7 +74,7 @@ export interface IMessage { emoji?: string; attachments?: IAttachment[]; urls?: IUrl[]; - _updatedAt: Date; + _updatedAt: string | Date; status?: number; pinned?: boolean; starred?: boolean; @@ -83,10 +83,10 @@ export interface IMessage { role?: string; drid?: string; dcount?: number; - dlm?: Date; + dlm?: string | Date; tmid?: string; tcount?: number; - tlm?: Date; + tlm?: string | Date; replies?: string[]; mentions?: IUserMention[]; channels?: IUserChannel[]; diff --git a/app/definitions/IRocketChatRecord.ts b/app/definitions/IRocketChatRecord.ts index bed6d1147..8a933059a 100644 --- a/app/definitions/IRocketChatRecord.ts +++ b/app/definitions/IRocketChatRecord.ts @@ -1,6 +1,6 @@ export interface IRocketChatRecord { - _id: string; - _updatedAt: Date; + _id?: string; + _updatedAt?: Date; } export type RocketChatRecordDeleted = T & diff --git a/app/definitions/IRoom.ts b/app/definitions/IRoom.ts index 11130215b..1f943cdb3 100644 --- a/app/definitions/IRoom.ts +++ b/app/definitions/IRoom.ts @@ -1,5 +1,7 @@ import Model from '@nozbe/watermelondb/Model'; +import { MarkdownAST } from '@rocket.chat/message-parser'; +import { IAttachment } from './IAttachment'; import { IMessage } from './IMessage'; import { IServedBy } from './IServedBy'; import { SubscriptionType } from './ISubscription'; @@ -37,6 +39,7 @@ export interface IRoom { tags?: string[]; e2eKeyId?: string; avatarETag?: string; + latest?: string; default?: true; featured?: true; } @@ -100,3 +103,52 @@ export interface IOmnichannelRoom extends Omit { _id: string; - createdAt: Date; - roles: string[]; - type: string; - active: boolean; - name?: string; + id: string; + token: string; + createdAt?: Date; + roles?: string[]; + type?: string; + active?: boolean; username: string; + name?: string; services?: IUserServices; emails?: IUserEmail[]; status?: UserStatus; @@ -115,7 +118,6 @@ export interface IUser extends IRocketChatRecord { oauth?: { authorizedClients: string[]; }; - _updatedAt: Date; statusLivechat?: string; e2e?: { private_key: string; diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index e23f5e321..7b4e52db0 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -23,7 +23,9 @@ import { ICreateChannel } from '../../reducers/createChannel'; import { ICreateDiscussion } from '../../reducers/createDiscussion'; import { IEncryption } from '../../reducers/encryption'; import { IInviteLinks } from '../../reducers/inviteLinks'; +import { ILogin } from '../../reducers/login'; import { IRoles } from '../../reducers/roles'; +import { IRoom } from '../../reducers/room'; import { ISelectedUsers } from '../../reducers/selectedUsers'; import { IServer } from '../../reducers/server'; import { ISettings } from '../../reducers/settings'; @@ -33,13 +35,13 @@ import { IEnterpriseModules } from '../../reducers/enterpriseModules'; export interface IApplicationState { settings: ISettings; - login: any; meteor: IConnect; + login: ILogin; server: IServer; selectedUsers: ISelectedUsers; app: IApp; createChannel: ICreateChannel; - room: any; + room: IRoom; rooms: any; sortPreferences: any; share: IShare; diff --git a/app/definitions/rest/v1/e2e.ts b/app/definitions/rest/v1/e2e.ts new file mode 100644 index 000000000..4e41ef562 --- /dev/null +++ b/app/definitions/rest/v1/e2e.ts @@ -0,0 +1,5 @@ +export type E2eEndpoints = { + 'e2e.setUserPublicAndPrivateKeys': { + POST: (params: { public_key: string; private_key: string }) => void; + }; +}; diff --git a/app/definitions/rest/v1/emojiCustom.ts b/app/definitions/rest/v1/emojiCustom.ts index 8ef956e5a..086589f2d 100644 --- a/app/definitions/rest/v1/emojiCustom.ts +++ b/app/definitions/rest/v1/emojiCustom.ts @@ -9,7 +9,7 @@ export type EmojiCustomEndpoints = { } & PaginatedResult; }; 'emoji-custom.list': { - GET: (params: { query: string }) => { + GET: (params: { updatedSince: string }) => { emojis?: { update: ICustomEmojiDescriptor[]; }; diff --git a/app/definitions/rest/v1/index.ts b/app/definitions/rest/v1/index.ts index ae5f29262..83bb410d0 100644 --- a/app/definitions/rest/v1/index.ts +++ b/app/definitions/rest/v1/index.ts @@ -14,6 +14,7 @@ import { OauthCustomConfiguration } from './settings'; import { UserEndpoints } from './user'; import { UsersEndpoints } from './users'; import { TeamsEndpoints } from './teams'; +import { E2eEndpoints } from './e2e'; export type Endpoints = ChannelsEndpoints & ChatEndpoints & @@ -30,4 +31,5 @@ export type Endpoints = ChannelsEndpoints & OauthCustomConfiguration & UserEndpoints & UsersEndpoints & - TeamsEndpoints; + TeamsEndpoints & + E2eEndpoints; diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index 43fe78dc0..4cae91c18 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -788,5 +788,24 @@ "Enable_Message_Parser": "Enable Message Parser", "Unsupported_format": "Unsupported format", "Downloaded_file": "Downloaded file", - "Error_Download_file": "Error while downloading file" + "Error_Download_file": "Error while downloading file", + "added__roomName__to_team": "added #{{roomName}} to this Team", + "Added__username__to_team": "added @{{user_added}} to this Team", + "Converted__roomName__to_team": "converted #{{roomName}} to a Team", + "Converted__roomName__to_channel": "converted #{{roomName}} to a Channel", + "Converting_team_to_channel": "Converting Team to Channel", + "Deleted__roomName__": "deleted #{{roomName}}", + "Message_HideType_added_user_to_team": "Hide \"User Added to Team\" messages", + "Message_HideType_removed_user_from_team": "Hide \"User Removed from Team\" messages", + "Message_HideType_ujt": "Hide \"User Joined Team\" messages", + "Message_HideType_ult": "Hide \"User Left Team\" messages", + "Message_HideType_user_added_room_to_team": "Hide \"User Added Room to Team\" messages", + "Message_HideType_user_converted_to_channel": "Hide \"User converted team to a Channel\" messages", + "Message_HideType_user_converted_to_team": "Hide \"User converted channel to a Team\" messages", + "Message_HideType_user_deleted_room_from_team": "Hide \"User deleted room from Team\" messages", + "Message_HideType_user_removed_room_from_team": "Hide \"User removed room from Team\" messages", + "Removed__roomName__from_this_team": "removed #{{roomName}} from this Team", + "Removed__username__from_team": "removed @{{user_removed}} from this Team", + "User_joined_team": "joined this Team", + "User_left_team": "left this Team" } \ No newline at end of file diff --git a/app/lib/encryption/encryption.ts b/app/lib/encryption/encryption.ts index 8d17ebd46..be979e278 100644 --- a/app/lib/encryption/encryption.ts +++ b/app/lib/encryption/encryption.ts @@ -443,7 +443,7 @@ class Encryption { }; // Decrypt a message - decryptMessage = async (message: Partial) => { + decryptMessage = async (message: Pick) => { const { t, e2e } = message; // Prevent create a new instance if this room was encrypted sometime ago diff --git a/app/lib/methods/callJitsi.js b/app/lib/methods/callJitsi.ts similarity index 62% rename from app/lib/methods/callJitsi.js rename to app/lib/methods/callJitsi.ts index b554660ed..b04fbb19d 100644 --- a/app/lib/methods/callJitsi.js +++ b/app/lib/methods/callJitsi.ts @@ -1,9 +1,11 @@ -import { store as reduxStore } from '../auxStore'; -import Navigation from '../Navigation'; +import { ISubscription } from '../../definitions'; import { events, logEvent } from '../../utils/log'; +import { store } from '../auxStore'; +import Navigation from '../Navigation'; +import sdk from '../rocketchat'; -async function jitsiURL({ room }) { - const { settings } = reduxStore.getState(); +async function jitsiURL({ room }: { room: ISubscription }) { + const { settings } = store.getState(); const { Jitsi_Enabled } = settings; if (!Jitsi_Enabled) { @@ -19,7 +21,7 @@ async function jitsiURL({ room }) { let queryString = ''; if (Jitsi_Enabled_TokenAuth) { try { - const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', room?.rid); + const accessToken = await sdk.methodCallWrapper('jitsi:generateAccessToken', room?.rid); queryString = `?jwt=${accessToken}`; } catch { logEvent(events.RA_JITSI_F); @@ -30,23 +32,23 @@ async function jitsiURL({ room }) { if (Jitsi_URL_Room_Hash) { rname = uniqueID + room?.rid; } else { - rname = encodeURIComponent(room.t === 'd' ? room?.usernames?.join?.(' x ') : room?.name); + rname = encodeURIComponent(room.t === 'd' ? (room?.usernames?.join?.(' x ') as string) : room?.name); } return `${protocol}${domain}${prefix}${rname}${queryString}`; } -export function callJitsiWithoutServer(path) { +export function callJitsiWithoutServer(path: string): void { logEvent(events.RA_JITSI_VIDEO); - const { Jitsi_SSL } = reduxStore.getState().settings; + const { Jitsi_SSL } = store.getState().settings; const protocol = Jitsi_SSL ? 'https://' : 'http://'; const url = `${protocol}${path}`; Navigation.navigate('JitsiMeetView', { url, onlyAudio: false }); } -async function callJitsi(room, onlyAudio = false) { +async function callJitsi(room: ISubscription, onlyAudio = false): Promise { logEvent(onlyAudio ? events.RA_JITSI_AUDIO : events.RA_JITSI_VIDEO); - const url = await jitsiURL.call(this, { room }); + const url = await jitsiURL({ room }); Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid: room?.rid }); } diff --git a/app/lib/methods/canOpenRoom.js b/app/lib/methods/canOpenRoom.ts similarity index 66% rename from app/lib/methods/canOpenRoom.js rename to app/lib/methods/canOpenRoom.ts index 183b32a36..7d3e72687 100644 --- a/app/lib/methods/canOpenRoom.js +++ b/app/lib/methods/canOpenRoom.ts @@ -1,5 +1,8 @@ -import database from '../database'; +import { ERoomTypes } from '../../definitions'; import { store } from '../auxStore'; +import database from '../database'; +import RocketChat from '../rocketchat'; +import sdk from '../rocketchat/services/sdk'; const restTypes = { channel: 'channels', @@ -7,27 +10,28 @@ const restTypes = { group: 'groups' }; -async function open({ type, rid, name }) { +async function open({ type, rid, name }: { type: ERoomTypes; rid: string; name: string }) { try { const params = rid ? { roomId: rid } : { roomName: name }; // if it's a direct link without rid we'll create a new dm // if the dm already exists it'll return the existent - if (type === 'direct' && !rid) { - const result = await this.createDirectMessage(name); + if (type === ERoomTypes.DIRECT && !rid) { + const result = await RocketChat.createDirectMessage(name); if (result.success) { const { room } = result; - room.rid = room._id; + room.rid = room._id as string; return room; } } // if it's a group we need to check if you can open - if (type === 'group') { + if (type === ERoomTypes.GROUP) { try { // RC 0.61.0 - await this.sdk.post(`${restTypes[type]}.open`, params); - } catch (e) { + // @ts-ignore + await sdk.post(`${restTypes[type]}.open`, params); + } catch (e: any) { if (!(e.data && /is already open/.test(e.data.error))) { return false; } @@ -36,9 +40,10 @@ async function open({ type, rid, name }) { // if it's a channel or group and the link don't have rid // we'll get info from the room - if ((type === 'channel' || type === 'group') && !rid) { + if ((type === ERoomTypes.CHANNEL || type === ERoomTypes.GROUP) && !rid) { // RC 0.72.0 - const result = await this.sdk.get(`${restTypes[type]}.info`, params); + // @ts-ignore + const result: any = await sdk.get(`channel.info`, params); if (result.success) { const room = result[type]; room.rid = room._id; @@ -56,7 +61,7 @@ async function open({ type, rid, name }) { } } -export default async function canOpenRoom({ rid, path, isCall }) { +export default async function canOpenRoom({ rid, path, isCall }: { rid: string; isCall: boolean; path: string }): Promise { try { const db = database.active; const subsCollection = db.get('subscriptions'); @@ -86,8 +91,9 @@ export default async function canOpenRoom({ rid, path, isCall }) { } const [type, name] = path.split('/'); + const t = type as ERoomTypes; try { - const result = await open.call(this, { type, rid, name }); + const result = await open({ type: t, rid, name }); return result; } catch (e) { return false; diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.ts similarity index 82% rename from app/lib/methods/enterpriseModules.js rename to app/lib/methods/enterpriseModules.ts index 34313074d..8769e3818 100644 --- a/app/lib/methods/enterpriseModules.js +++ b/app/lib/methods/enterpriseModules.ts @@ -1,11 +1,12 @@ +import sdk from '../rocketchat/services/sdk'; import { compareServerVersion } from '../utils'; import { store as reduxStore } from '../auxStore'; import database from '../database'; import log from '../../utils/log'; import { clearEnterpriseModules, setEnterpriseModules as setEnterpriseModulesAction } from '../../actions/enterpriseModules'; -export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise'; -export const LICENSE_LIVECHAT_ENTERPRISE = 'livechat-enterprise'; +const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise'; +const LICENSE_LIVECHAT_ENTERPRISE = 'livechat-enterprise'; export async function setEnterpriseModules() { try { @@ -29,17 +30,17 @@ export async function setEnterpriseModules() { } export function getEnterpriseModules() { - return new Promise(async resolve => { + return new Promise(async resolve => { try { const { version: serverVersion, server: serverId } = reduxStore.getState().server; if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '3.1.0')) { // RC 3.1.0 - const enterpriseModules = await this.methodCallWrapper('license:getModules'); + const enterpriseModules = await sdk.methodCallWrapper('license:getModules'); if (enterpriseModules) { const serversDB = database.servers; const serversCollection = serversDB.get('servers'); const server = await serversCollection.find(serverId); - await serversDB.action(async () => { + await serversDB.write(async () => { await server.update(s => { s.enterpriseModules = enterpriseModules.join(','); }); @@ -56,7 +57,7 @@ export function getEnterpriseModules() { }); } -export function hasLicense(module) { +export function hasLicense(module: string) { const { enterpriseModules } = reduxStore.getState(); return enterpriseModules.includes(module); } diff --git a/app/lib/methods/getCustomEmojis.js b/app/lib/methods/getCustomEmojis.ts similarity index 51% rename from app/lib/methods/getCustomEmojis.js rename to app/lib/methods/getCustomEmojis.ts index 55f7c4c44..87a549d18 100644 --- a/app/lib/methods/getCustomEmojis.js +++ b/app/lib/methods/getCustomEmojis.ts @@ -1,13 +1,22 @@ import orderBy from 'lodash/orderBy'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; +import { ICustomEmojis } from '../../reducers/customEmojis'; import { compareServerVersion } from '../utils'; import { store as reduxStore } from '../auxStore'; import database from '../database'; import log from '../../utils/log'; import { setCustomEmojis as setCustomEmojisAction } from '../../actions/customEmojis'; +import { ICustomEmoji, TCustomEmojiModel } from '../../definitions'; +import sdk from '../rocketchat/services/sdk'; -const getUpdatedSince = allEmojis => { +interface IUpdateEmojis { + update: TCustomEmojiModel[]; + remove?: TCustomEmojiModel[]; + allRecords: TCustomEmojiModel[]; +} + +const getUpdatedSince = (allEmojis: ICustomEmoji[]) => { if (!allEmojis.length) { return null; } @@ -19,27 +28,27 @@ const getUpdatedSince = allEmojis => { return ordered && ordered[0]._updatedAt.toISOString(); }; -const updateEmojis = async ({ update = [], remove = [], allRecords }) => { +const updateEmojis = async ({ update = [], remove = [], allRecords }: IUpdateEmojis) => { if (!((update && update.length) || (remove && remove.length))) { return; } const db = database.active; const emojisCollection = db.get('custom_emojis'); - let emojisToCreate = []; - let emojisToUpdate = []; - let emojisToDelete = []; + let emojisToCreate: TCustomEmojiModel[] = []; + let emojisToUpdate: TCustomEmojiModel[] = []; + let emojisToDelete: TCustomEmojiModel[] = []; // Create or update if (update && update.length) { - emojisToCreate = update.filter(i1 => !allRecords.find(i2 => i1._id === i2.id)); - emojisToUpdate = allRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); - emojisToCreate = emojisToCreate.map(emoji => + const filterEmojisToCreate = update.filter(i1 => !allRecords.find(i2 => i1._id === i2.id)); + const filterEmojisToUpdate = allRecords.filter(i1 => update.find(i2 => i1.id === i2._id)); + emojisToCreate = filterEmojisToCreate.map(emoji => emojisCollection.prepareCreate(e => { e._raw = sanitizedRaw({ id: emoji._id }, emojisCollection.schema); Object.assign(e, emoji); }) ); - emojisToUpdate = emojisToUpdate.map(emoji => { + emojisToUpdate = filterEmojisToUpdate.map(emoji => { const newEmoji = update.find(e => e._id === emoji.id); return emoji.prepareUpdate(e => { Object.assign(e, newEmoji); @@ -48,12 +57,12 @@ const updateEmojis = async ({ update = [], remove = [], allRecords }) => { } if (remove && remove.length) { - emojisToDelete = allRecords.filter(i1 => remove.find(i2 => i1.id === i2._id)); - emojisToDelete = emojisToDelete.map(emoji => emoji.prepareDestroyPermanently()); + const filterEmojisToDelete = allRecords.filter(i1 => remove.find(i2 => i1.id === i2._id)); + emojisToDelete = filterEmojisToDelete.map(emoji => emoji.prepareDestroyPermanently()); } try { - await db.action(async () => { + await db.write(async () => { await db.batch(...emojisToCreate, ...emojisToUpdate, ...emojisToDelete); }); return true; @@ -66,26 +75,34 @@ export async function setCustomEmojis() { const db = database.active; const emojisCollection = db.get('custom_emojis'); const allEmojis = await emojisCollection.query().fetch(); - const parsed = allEmojis.reduce((ret, item) => { - ret[item.name] = { - name: item.name, - extension: item.extension - }; - item.aliases.forEach(alias => { - ret[alias] = { + const parsed = allEmojis.reduce((ret: ICustomEmojis, item) => { + if (item.name) { + ret[item.name] = { name: item.name, extension: item.extension }; - }); + } + + if (item.aliases) { + item.aliases.forEach(alias => { + if (item.name) { + ret[alias] = { + name: item.name, + extension: item.extension + }; + } + }); + } + return ret; }, {}); reduxStore.dispatch(setCustomEmojisAction(parsed)); } export function getCustomEmojis() { - return new Promise(async resolve => { + return new Promise(async resolve => { try { - const serverVersion = reduxStore.getState().server.version; + const serverVersion = reduxStore.getState().server.version as string; const db = database.active; const emojisCollection = db.get('custom_emojis'); const allRecords = await emojisCollection.query().fetch(); @@ -94,10 +111,11 @@ export function getCustomEmojis() { // if server version is lower than 0.75.0, fetches from old api if (compareServerVersion(serverVersion, 'lowerThan', '0.75.0')) { // RC 0.61.0 - const result = await this.sdk.get('emoji-custom'); - + // @ts-ignore + const result = await sdk.get('emoji-custom'); + // @ts-ignore let { emojis } = result; - emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince); + emojis = emojis.filter((emoji: TCustomEmojiModel) => !updatedSince || emoji._updatedAt.toISOString() > updatedSince); const changedEmojis = await updateEmojis({ update: emojis, allRecords }); // `setCustomEmojis` is fired on selectServer @@ -106,28 +124,28 @@ export function getCustomEmojis() { setCustomEmojis(); } return resolve(); - } else { - const params = {}; - if (updatedSince) { - params.updatedSince = updatedSince; - } + } + const params: { updatedSince: string } = { updatedSince: '' }; + if (updatedSince) { + params.updatedSince = updatedSince; + } - // RC 0.75.0 - const result = await this.sdk.get('emoji-custom.list', params); + // RC 0.75.0 + const result = await sdk.get('emoji-custom.list', params); - if (!result.success) { - return resolve(); - } + if (!result.success) { + return resolve(); + } - const { emojis } = result; - const { update, remove } = emojis; - const changedEmojis = await updateEmojis({ update, remove, allRecords }); + const { emojis } = result; + // @ts-ignore + const { update, remove } = emojis; + const changedEmojis = await updateEmojis({ update, remove, allRecords }); - // `setCustomEmojis` is fired on selectServer - // We run it again only if emojis were changed - if (changedEmojis) { - setCustomEmojis(); - } + // `setCustomEmojis` is fired on selectServer + // We run it again only if emojis were changed + if (changedEmojis) { + setCustomEmojis(); } } catch (e) { log(e); diff --git a/app/lib/methods/getRoles.js b/app/lib/methods/getRoles.ts similarity index 71% rename from app/lib/methods/getRoles.js rename to app/lib/methods/getRoles.ts index 5f9c91ee5..6add9e897 100644 --- a/app/lib/methods/getRoles.js +++ b/app/lib/methods/getRoles.ts @@ -1,4 +1,5 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; +import Model from '@nozbe/watermelondb/Model'; import database from '../database'; import { getRoleById } from '../database/services/Role'; @@ -6,8 +7,10 @@ import log from '../../utils/log'; import { store as reduxStore } from '../auxStore'; import { removeRoles, setRoles as setRolesAction, updateRoles } from '../../actions/roles'; import protectedFunction from './helpers/protectedFunction'; +import { TRoleModel } from '../../definitions'; +import sdk from '../rocketchat/services/sdk'; -export async function setRoles() { +export async function setRoles(): Promise { const db = database.active; const rolesCollection = db.get('roles'); const allRoles = await rolesCollection.query().fetch(); @@ -15,7 +18,17 @@ export async function setRoles() { reduxStore.dispatch(setRolesAction(parsed)); } -export async function onRolesChanged(ddpMessage) { +interface IRolesChanged { + fields: { + args: { + type: string; + _id: string; + description: string; + }[]; + }; +} + +export async function onRolesChanged(ddpMessage: IRolesChanged): Promise { const { type, _id, description } = ddpMessage.fields.args[0]; if (/changed/.test(type)) { const db = database.active; @@ -42,7 +55,6 @@ export async function onRolesChanged(ddpMessage) { } if (/removed/.test(type)) { const db = database.active; - const rolesCollection = db.get('roles'); try { const roleRecord = await getRoleById(_id); if (roleRecord) { @@ -57,12 +69,12 @@ export async function onRolesChanged(ddpMessage) { } } -export function getRoles() { +export function getRoles(): Promise { const db = database.active; return new Promise(async resolve => { try { // RC 0.70.0 - const result = await this.sdk.get('roles.list'); + const result = await sdk.get('roles.list'); if (!result.success) { return resolve(); @@ -71,18 +83,18 @@ export function getRoles() { const { roles } = result; if (roles && roles.length) { - await db.action(async () => { + await db.write(async () => { const rolesCollections = db.get('roles'); const allRolesRecords = await rolesCollections.query().fetch(); // filter roles - let rolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id)); - let rolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id)); + const filteredRolesToCreate = roles.filter(i1 => !allRolesRecords.find(i2 => i1._id === i2.id)); + const filteredRolesToUpdate = allRolesRecords.filter(i1 => roles.find(i2 => i1.id === i2._id)); // Create - rolesToCreate = rolesToCreate.map(role => + const rolesToCreate = filteredRolesToCreate.map(role => rolesCollections.prepareCreate( - protectedFunction(r => { + protectedFunction((r: TRoleModel) => { r._raw = sanitizedRaw({ id: role._id }, rolesCollections.schema); Object.assign(r, role); }) @@ -90,16 +102,16 @@ export function getRoles() { ); // Update - rolesToUpdate = rolesToUpdate.map(role => { + const rolesToUpdate = filteredRolesToUpdate.map(role => { const newRole = roles.find(r => r._id === role.id); return role.prepareUpdate( - protectedFunction(r => { + protectedFunction((r: TRoleModel) => { Object.assign(r, newRole); }) ); }); - const allRecords = [...rolesToCreate, ...rolesToUpdate]; + const allRecords: Model[] = [...rolesToCreate, ...rolesToUpdate]; try { await db.batch(...allRecords); diff --git a/app/lib/methods/getRoomInfo.js b/app/lib/methods/getRoomInfo.ts similarity index 77% rename from app/lib/methods/getRoomInfo.js rename to app/lib/methods/getRoomInfo.ts index 24c061aa0..b74dbaca1 100644 --- a/app/lib/methods/getRoomInfo.js +++ b/app/lib/methods/getRoomInfo.ts @@ -1,7 +1,8 @@ +import { IRoom } from '../../definitions'; import { getSubscriptionByRoomId } from '../database/services/Subscription'; import RocketChat from '../rocketchat'; -const getRoomInfo = async rid => { +const getRoomInfo = async (rid: string): Promise | null> => { let result; result = await getSubscriptionByRoomId(rid); if (result) { diff --git a/app/lib/methods/getRooms.ts b/app/lib/methods/getRooms.ts index e4f369975..8d7f89397 100644 --- a/app/lib/methods/getRooms.ts +++ b/app/lib/methods/getRooms.ts @@ -6,8 +6,12 @@ export default function (updatedSince: Date) { if (updatedSince) { const updatedDate = updatedSince.toISOString(); // TODO: missing definitions from server - // @ts-ignore - return Promise.all([sdk.get('subscriptions.get', { updatedDate }), sdk.get('rooms.get', { updatedDate })]); + return Promise.all([ + // @ts-ignore + sdk.get('subscriptions.get', { updatedSince: updatedDate }), + // @ts-ignore + sdk.get('rooms.get', { updatedSince: updatedDate }) + ]); } // TODO: missing definitions from server // @ts-ignore diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.ts similarity index 75% rename from app/lib/methods/getSettings.js rename to app/lib/methods/getSettings.ts index bd5cdfbc3..2ef89b603 100644 --- a/app/lib/methods/getSettings.js +++ b/app/lib/methods/getSettings.ts @@ -1,14 +1,16 @@ -import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { Q } from '@nozbe/watermelondb'; +import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { addSettings, clearSettings } from '../../actions/settings'; -import RocketChat from '../rocketchat'; -import { store as reduxStore } from '../auxStore'; -import settings from '../../constants/settings'; -import log from '../../utils/log'; -import database from '../database'; -import fetch from '../../utils/fetch'; import { DEFAULT_AUTO_LOCK } from '../../constants/localAuthentication'; +import settings from '../../constants/settings'; +import { IPreparedSettings, ISettingsIcon } from '../../definitions'; +import fetch from '../../utils/fetch'; +import log from '../../utils/log'; +import { store as reduxStore } from '../auxStore'; +import database from '../database'; +import RocketChat from '../rocketchat'; +import sdk from '../rocketchat/services/sdk'; import protectedFunction from './helpers/protectedFunction'; const serverInfoKeys = [ @@ -41,7 +43,7 @@ const loginSettings = [ 'Accounts_Iframe_api_method' ]; -const serverInfoUpdate = async (serverInfo, iconSetting) => { +const serverInfoUpdate = async (serverInfo: IPreparedSettings[], iconSetting: ISettingsIcon) => { const serversDB = database.servers; const serverId = reduxStore.getState().server.server; const serversCollection = serversDB.get('servers'); @@ -73,7 +75,7 @@ const serverInfoUpdate = async (serverInfo, iconSetting) => { return { ...allSettings, autoLockTime: DEFAULT_AUTO_LOCK }; } // if Force_Screen_Lock_After > 0 and forceScreenLock is enabled, use it - if (setting.valueAsNumber > 0 && forceScreenLock) { + if (setting.valueAsNumber && setting.valueAsNumber > 0 && forceScreenLock) { return { ...allSettings, autoLockTime: setting.valueAsNumber }; } } @@ -91,7 +93,7 @@ const serverInfoUpdate = async (serverInfo, iconSetting) => { info = { ...info, iconURL }; } - await serversDB.action(async () => { + await serversDB.write(async () => { try { await server.update(record => { Object.assign(record, info); @@ -102,7 +104,7 @@ const serverInfoUpdate = async (serverInfo, iconSetting) => { }); }; -export async function getLoginSettings({ server }) { +export async function getLoginSettings({ server }: { server: string }): Promise { try { const settingsParams = JSON.stringify(loginSettings); const result = await fetch(`${server}/api/v1/settings.public?query={"_id":{"$in":${settingsParams}}}`).then(response => @@ -111,14 +113,14 @@ export async function getLoginSettings({ server }) { if (result.success && result.settings.length) { reduxStore.dispatch(clearSettings()); - reduxStore.dispatch(addSettings(this.parseSettings(this._prepareSettings(result.settings)))); + reduxStore.dispatch(addSettings(RocketChat.parseSettings(RocketChat._prepareSettings(result.settings)))); } } catch (e) { log(e); } } -export async function setSettings() { +export async function setSettings(): Promise { const db = database.active; const settingsCollection = db.get('settings'); const settingsRecords = await settingsCollection.query().fetch(); @@ -133,17 +135,19 @@ export async function setSettings() { reduxStore.dispatch(addSettings(RocketChat.parseSettings(parsed.slice(0, parsed.length)))); } -export function subscribeSettings() { +export function subscribeSettings(): void { return RocketChat.subscribe('stream-notify-all', 'public-settings-changed'); } -export default async function () { +type IData = ISettingsIcon | IPreparedSettings; + +export default async function (): Promise { try { const db = database.active; const settingsParams = Object.keys(settings).filter(key => !loginSettings.includes(key)); // RC 0.60.0 const result = await fetch( - `${this.sdk.client.host}/api/v1/settings.public?query={"_id":{"$in":${JSON.stringify(settingsParams)}}}&count=${ + `${sdk.current.client.host}/api/v1/settings.public?query={"_id":{"$in":${JSON.stringify(settingsParams)}}}&count=${ settingsParams.length }` ).then(response => response.json()); @@ -151,33 +155,32 @@ export default async function () { if (!result.success) { return; } - const data = result.settings || []; - const filteredSettings = this._prepareSettings(data); + const data: IData[] = result.settings || []; + const filteredSettings: IPreparedSettings[] = RocketChat._prepareSettings(data); const filteredSettingsIds = filteredSettings.map(s => s._id); - reduxStore.dispatch(addSettings(this.parseSettings(filteredSettings))); + reduxStore.dispatch(addSettings(RocketChat.parseSettings(filteredSettings))); // filter server info const serverInfo = filteredSettings.filter(i1 => serverInfoKeys.includes(i1._id)); - const iconSetting = data.find(item => item._id === 'Assets_favicon_512'); + const iconSetting = data.find(icon => icon._id === 'Assets_favicon_512'); try { - await serverInfoUpdate(serverInfo, iconSetting); + await serverInfoUpdate(serverInfo, iconSetting as ISettingsIcon); } catch { // Server not found } - await db.action(async () => { + await db.write(async () => { const settingsCollection = db.get('settings'); const allSettingsRecords = await settingsCollection.query(Q.where('id', Q.oneOf(filteredSettingsIds))).fetch(); // filter settings - let settingsToCreate = filteredSettings.filter(i1 => !allSettingsRecords.find(i2 => i1._id === i2.id)); - let settingsToUpdate = allSettingsRecords.filter(i1 => filteredSettings.find(i2 => i1.id === i2._id)); - + const settingsToCreate = filteredSettings.filter(i1 => !allSettingsRecords.find(i2 => i1._id === i2.id)); + const settingsToUpdate = allSettingsRecords.filter(i1 => filteredSettings.find(i2 => i1.id === i2._id)); // Create - settingsToCreate = settingsToCreate.map(setting => + const settingsToCreateMapped = settingsToCreate.map(setting => settingsCollection.prepareCreate( - protectedFunction(s => { + protectedFunction((s: any) => { s._raw = sanitizedRaw({ id: setting._id }, settingsCollection.schema); Object.assign(s, setting); }) @@ -185,16 +188,16 @@ export default async function () { ); // Update - settingsToUpdate = settingsToUpdate.map(setting => { + const settingsToUpdateMapped = settingsToUpdate.map(setting => { const newSetting = filteredSettings.find(s => s._id === setting.id); return setting.prepareUpdate( - protectedFunction(s => { + protectedFunction((s: any) => { Object.assign(s, newSetting); }) ); }); - const allRecords = [...settingsToCreate, ...settingsToUpdate]; + const allRecords = [...settingsToCreateMapped, ...settingsToUpdateMapped]; try { await db.batch(...allRecords); diff --git a/app/lib/methods/getSingleMessage.ts b/app/lib/methods/getSingleMessage.ts index 4d395a0db..cc153e2f4 100644 --- a/app/lib/methods/getSingleMessage.ts +++ b/app/lib/methods/getSingleMessage.ts @@ -1,6 +1,7 @@ import RocketChat from '../rocketchat'; +import { IMessage } from '../../definitions'; -const getSingleMessage = (messageId: string) => +const getSingleMessage = (messageId: string): Promise => new Promise(async (resolve, reject) => { try { const result = await RocketChat.getSingleMessage(messageId); diff --git a/app/lib/methods/getSlashCommands.js b/app/lib/methods/getSlashCommands.js deleted file mode 100644 index 3b65c40eb..000000000 --- a/app/lib/methods/getSlashCommands.js +++ /dev/null @@ -1,71 +0,0 @@ -import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; - -import database from '../database'; -import log from '../../utils/log'; -import protectedFunction from './helpers/protectedFunction'; - -export default function () { - const db = database.active; - return new Promise(async resolve => { - try { - // RC 0.60.2 - const result = await this.sdk.get('commands.list'); - - if (!result.success) { - console.log(result); - return resolve(); - } - - const { commands } = result; - - if (commands && commands.length) { - await db.action(async () => { - const slashCommandsCollection = db.get('slash_commands'); - const allSlashCommandsRecords = await slashCommandsCollection.query().fetch(); - - // filter slash commands - let slashCommandsToCreate = commands.filter(i1 => !allSlashCommandsRecords.find(i2 => i1.command === i2.id)); - let slashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => commands.find(i2 => i1.id === i2.command)); - let slashCommandsToDelete = allSlashCommandsRecords.filter( - i1 => !slashCommandsToCreate.find(i2 => i2.command === i1.id) && !slashCommandsToUpdate.find(i2 => i2.id === i1.id) - ); - - // Create - slashCommandsToCreate = slashCommandsToCreate.map(command => - slashCommandsCollection.prepareCreate( - protectedFunction(s => { - s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema); - Object.assign(s, command); - }) - ) - ); - - // Update - slashCommandsToUpdate = slashCommandsToUpdate.map(command => { - const newCommand = commands.find(s => s.command === command.id); - return command.prepareUpdate( - protectedFunction(s => { - Object.assign(s, newCommand); - }) - ); - }); - - // Delete - slashCommandsToDelete = slashCommandsToDelete.map(command => command.prepareDestroyPermanently()); - - const allRecords = [...slashCommandsToCreate, ...slashCommandsToUpdate, ...slashCommandsToDelete]; - - try { - await db.batch(...allRecords); - } catch (e) { - log(e); - } - return allRecords.length; - }); - } - } catch (e) { - log(e); - return resolve(); - } - }); -} diff --git a/app/lib/methods/getSlashCommands.ts b/app/lib/methods/getSlashCommands.ts new file mode 100644 index 000000000..ed9f37783 --- /dev/null +++ b/app/lib/methods/getSlashCommands.ts @@ -0,0 +1,78 @@ +import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; + +import database from '../database'; +import log from '../../utils/log'; +import protectedFunction from './helpers/protectedFunction'; +import { ISlashCommandResult, TSlashCommandModel } from '../../definitions'; +import sdk from '../rocketchat/services/sdk'; + +export default function getSlashCommands() { + const db = database.active; + return new Promise(async resolve => { + try { + // RC 0.60.2 + // @ts-ignore + const result = await sdk.get('commands.list'); + + if (!result.success) { + return resolve(); + } + // @ts-ignore + const { commands } = result; + if (commands && commands.length) { + await db.write(async () => { + const slashCommandsCollection = db.get('slash_commands'); + const allSlashCommandsRecords = await slashCommandsCollection.query().fetch(); + + // filter slash commands + const filteredSlashCommandsToCreate = commands.filter( + (i1: ISlashCommandResult) => !allSlashCommandsRecords.find(i2 => i1.command === i2.id) + ); + const filteredSlashCommandsToUpdate = allSlashCommandsRecords.filter(i1 => + commands.find((i2: ISlashCommandResult) => i1.id === i2.command) + ); + const filteredSlashCommandsToDelete = allSlashCommandsRecords.filter( + i1 => + !filteredSlashCommandsToCreate.find((i2: ISlashCommandResult) => i2.command === i1.id) && + !filteredSlashCommandsToUpdate.find(i2 => i2.id === i1.id) + ); + + // Create + const slashCommandsToCreate = filteredSlashCommandsToCreate.map((command: ISlashCommandResult) => + slashCommandsCollection.prepareCreate( + protectedFunction((s: TSlashCommandModel) => { + s._raw = sanitizedRaw({ id: command.command }, slashCommandsCollection.schema); + Object.assign(s, command); + }) + ) + ); + + // Update + const slashCommandsToUpdate = filteredSlashCommandsToUpdate.map(command => { + const newCommand = commands.find((s: ISlashCommandResult) => s.command === command.id); + return command.prepareUpdate( + protectedFunction((s: TSlashCommandModel) => { + Object.assign(s, newCommand); + }) + ); + }); + + // Delete + const slashCommandsToDelete = filteredSlashCommandsToDelete.map(command => command.prepareDestroyPermanently()); + + const allRecords = [...slashCommandsToCreate, ...slashCommandsToUpdate, ...slashCommandsToDelete]; + + try { + await db.batch(...allRecords); + } catch (e) { + log(e); + } + return allRecords.length; + }); + } + } catch (e) { + log(e); + return resolve(); + } + }); +} diff --git a/app/lib/methods/getThreadName.js b/app/lib/methods/getThreadName.ts similarity index 69% rename from app/lib/methods/getThreadName.js rename to app/lib/methods/getThreadName.ts index a490a7d40..b6ed94580 100644 --- a/app/lib/methods/getThreadName.js +++ b/app/lib/methods/getThreadName.ts @@ -6,11 +6,12 @@ import { getThreadById } from '../database/services/Thread'; import log from '../../utils/log'; import { Encryption } from '../encryption'; import getSingleMessage from './getSingleMessage'; +import { IThread, TThreadModel } from '../../definitions'; -const buildThreadName = thread => thread.msg || thread?.attachments?.[0]?.title; +const buildThreadName = (thread: IThread): string | undefined => thread.msg || thread?.attachments?.[0]?.title; -const getThreadName = async (rid, tmid, messageId) => { - let tmsg; +const getThreadName = async (rid: string, tmid: string, messageId: string): Promise => { + let tmsg: string | undefined; try { const db = database.active; const threadCollection = db.get('threads'); @@ -18,7 +19,7 @@ const getThreadName = async (rid, tmid, messageId) => { const threadRecord = await getThreadById(tmid); if (threadRecord) { tmsg = buildThreadName(threadRecord); - await db.action(async () => { + await db.write(async () => { await messageRecord?.update(m => { m.tmsg = tmsg; }); @@ -27,11 +28,11 @@ const getThreadName = async (rid, tmid, messageId) => { let thread = await getSingleMessage(tmid); thread = await Encryption.decryptMessage(thread); tmsg = buildThreadName(thread); - await db.action(async () => { + await db.write(async () => { await db.batch( - threadCollection?.prepareCreate(t => { + threadCollection?.prepareCreate((t: TThreadModel) => { t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema); - t.subscription.id = rid; + if (t.subscription) t.subscription.id = rid; Object.assign(t, thread); }), messageRecord?.prepareUpdate(m => { diff --git a/app/lib/methods/getUsersPresence.js b/app/lib/methods/getUsersPresence.ts similarity index 68% rename from app/lib/methods/getUsersPresence.js rename to app/lib/methods/getUsersPresence.ts index f596e527a..afc885caa 100644 --- a/app/lib/methods/getUsersPresence.js +++ b/app/lib/methods/getUsersPresence.ts @@ -1,14 +1,17 @@ import { InteractionManager } from 'react-native'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; +import { IActiveUsers } from '../../reducers/activeUsers'; import { compareServerVersion } from '../utils'; import { store as reduxStore } from '../auxStore'; import { setActiveUsers } from '../../actions/activeUsers'; import { setUser } from '../../actions/login'; import database from '../database'; +import { IRocketChat, IUser } from '../../definitions'; +import sdk from '../rocketchat/services/sdk'; -export function subscribeUsersPresence() { - const serverVersion = reduxStore.getState().server.version; +export function subscribeUsersPresence(this: IRocketChat) { + const serverVersion = reduxStore.getState().server.version as string; // if server is lower than 1.1.0 if (compareServerVersion(serverVersion, 'lowerThan', '1.1.0')) { @@ -17,22 +20,22 @@ export function subscribeUsersPresence() { this.activeUsersSubTimeout = false; } this.activeUsersSubTimeout = setTimeout(() => { - this.sdk.subscribe('activeUsers'); + sdk.subscribe('activeUsers'); }, 5000); } else if (compareServerVersion(serverVersion, 'lowerThan', '4.1.0')) { - this.sdk.subscribe('stream-notify-logged', 'user-status'); + sdk.subscribe('stream-notify-logged', 'user-status'); } // RC 0.49.1 - this.sdk.subscribe('stream-notify-logged', 'updateAvatar'); + sdk.subscribe('stream-notify-logged', 'updateAvatar'); // RC 0.58.0 - this.sdk.subscribe('stream-notify-logged', 'Users:NameChanged'); + sdk.subscribe('stream-notify-logged', 'Users:NameChanged'); } -let ids = []; +let ids: string[] = []; export default async function getUsersPresence() { - const serverVersion = reduxStore.getState().server.version; + const serverVersion = reduxStore.getState().server.version as string; const { user: loggedUser } = reduxStore.getState().login; // if server is greather than or equal 1.1.0 @@ -51,17 +54,17 @@ export default async function getUsersPresence() { try { // RC 1.1.0 - const result = await this.sdk.get('users.presence', params); + const result = (await sdk.get('users.presence' as any, params as any)) as any; if (compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '4.1.0')) { - this.sdk.subscribeRaw('stream-user-presence', ['', { added: ids }]); + sdk.subscribeRaw('stream-user-presence', ['', { added: ids }]); } if (result.success) { const { users } = result; - const activeUsers = ids.reduce((ret, id) => { - const user = users.find(u => u._id === id) ?? { _id: id, status: 'offline' }; + const activeUsers = ids.reduce((ret: IActiveUsers, id) => { + const user = users.find((u: IUser) => u._id === id) ?? { _id: id, status: 'offline' }; const { _id, status, statusText } = user; if (loggedUser && loggedUser.id === _id) { @@ -78,17 +81,17 @@ export default async function getUsersPresence() { const db = database.active; const userCollection = db.get('users'); - users.forEach(async user => { + users.forEach(async (user: IUser) => { try { const userRecord = await userCollection.find(user._id); - await db.action(async () => { + await db.write(async () => { await userRecord.update(u => { Object.assign(u, user); }); }); } catch (e) { // User not found - await db.action(async () => { + await db.write(async () => { await userCollection.create(u => { u._raw = sanitizedRaw({ id: user._id }, userCollection.schema); Object.assign(u, user); @@ -103,11 +106,11 @@ export default async function getUsersPresence() { } } -let usersTimer = null; -export function getUserPresence(uid) { +let usersTimer: number | null = null; +export function getUserPresence(uid: string) { if (!usersTimer) { usersTimer = setTimeout(() => { - getUsersPresence.call(this); + getUsersPresence(); usersTimer = null; }, 2000); } diff --git a/app/lib/methods/helpers/findSubscriptionsRooms.js b/app/lib/methods/helpers/findSubscriptionsRooms.ts similarity index 74% rename from app/lib/methods/helpers/findSubscriptionsRooms.js rename to app/lib/methods/helpers/findSubscriptionsRooms.ts index e931b3edd..56d1ca058 100644 --- a/app/lib/methods/helpers/findSubscriptionsRooms.js +++ b/app/lib/methods/helpers/findSubscriptionsRooms.ts @@ -1,15 +1,18 @@ import { Q } from '@nozbe/watermelondb'; +import { IServerSubscriptionItem, IServerRoomItem } from '../../../definitions'; import database from '../../database'; -export default async (subscriptions = [], rooms = []) => { +export default async (subscriptions: IServerSubscriptionItem[], rooms: IServerRoomItem[]) => { + let sub = subscriptions; + let room = rooms; try { const db = database.active; const subCollection = db.get('subscriptions'); const roomIds = rooms.filter(r => !subscriptions.find(s => s.rid === r._id)).map(r => r._id); - let existingSubs = await subCollection.query(Q.where('rid', Q.oneOf(roomIds))).fetch(); - existingSubs = existingSubs.map(s => ({ + const existingSubs = await subCollection.query(Q.where('rid', Q.oneOf(roomIds))).fetch(); + const mappedExistingSubs = existingSubs.map(s => ({ _id: s._id, f: s.f, t: s.t, @@ -55,11 +58,12 @@ export default async (subscriptions = [], rooms = []) => { E2EKey: s.E2EKey, avatarETag: s.avatarETag })); - subscriptions = subscriptions.concat(existingSubs); + // Assign + sub = subscriptions.concat(mappedExistingSubs as unknown as IServerSubscriptionItem); const subsIds = subscriptions.filter(s => !rooms.find(r => s.rid === r._id)).map(s => s._id); - let existingRooms = await subCollection.query(Q.where('id', Q.oneOf(subsIds))).fetch(); - existingRooms = existingRooms.map(r => ({ + const existingRooms = await subCollection.query(Q.where('id', Q.oneOf(subsIds))).fetch(); + const mappedExistingRooms = existingRooms.map(r => ({ _updatedAt: r._updatedAt, lastMessage: r.lastMessage, description: r.description, @@ -84,13 +88,14 @@ export default async (subscriptions = [], rooms = []) => { e2eKeyId: r.e2eKeyId, avatarETag: r.avatarETag })); - rooms = rooms.concat(existingRooms); + // Assign + room = rooms.concat(mappedExistingRooms as unknown as IServerRoomItem); } catch { // do nothing } return { - subscriptions, - rooms + subscriptions: sub, + rooms: room }; }; diff --git a/app/lib/methods/helpers/getFileUrlFromMessage.js b/app/lib/methods/helpers/getFileUrlFromMessage.ts similarity index 80% rename from app/lib/methods/helpers/getFileUrlFromMessage.js rename to app/lib/methods/helpers/getFileUrlFromMessage.ts index c5a34ba04..ea3fb64ff 100644 --- a/app/lib/methods/helpers/getFileUrlFromMessage.js +++ b/app/lib/methods/helpers/getFileUrlFromMessage.ts @@ -1,4 +1,4 @@ -export default function (message) { +export default function (message: { type: string; url: string }) { if (/image/.test(message.type)) { return { image_url: message.url }; } diff --git a/app/lib/methods/helpers/mergeSubscriptionsRooms.js b/app/lib/methods/helpers/mergeSubscriptionsRooms.ts similarity index 65% rename from app/lib/methods/helpers/mergeSubscriptionsRooms.js rename to app/lib/methods/helpers/mergeSubscriptionsRooms.ts index e4541a426..4cca2707d 100644 --- a/app/lib/methods/helpers/mergeSubscriptionsRooms.js +++ b/app/lib/methods/helpers/mergeSubscriptionsRooms.ts @@ -5,17 +5,18 @@ import { store as reduxStore } from '../../auxStore'; import { compareServerVersion } from '../../utils'; import findSubscriptionsRooms from './findSubscriptionsRooms'; import normalizeMessage from './normalizeMessage'; +import { ISubscription, IServerRoom, IServerSubscription, IServerSubscriptionItem, IServerRoomItem } from '../../../definitions'; // TODO: delete and update -export const merge = (subscription, room) => { - const serverVersion = reduxStore.getState().server.version; - subscription = EJSON.fromJSONValue(subscription); - room = EJSON.fromJSONValue(room); +export const merge = ( + subscription: ISubscription | IServerSubscriptionItem, + room?: ISubscription | IServerRoomItem +): ISubscription => { + const serverVersion = reduxStore.getState().server.version as string; + subscription = EJSON.fromJSONValue(subscription) as ISubscription; - if (!subscription) { - return; - } if (room) { + room = EJSON.fromJSONValue(room) as ISubscription; if (room._updatedAt) { subscription.lastMessage = normalizeMessage(room.lastMessage); subscription.description = room.description; @@ -28,15 +29,21 @@ export const merge = (subscription, room) => { subscription.usernames = room.usernames; subscription.uids = room.uids; } + if (compareServerVersion(serverVersion, 'lowerThan', '3.7.0')) { const updatedAt = room?._updatedAt ? new Date(room._updatedAt) : null; const lastMessageTs = subscription?.lastMessage?.ts ? new Date(subscription.lastMessage.ts) : null; + // @ts-ignore + // If all parameters are null it will return zero, if only one is null it will return its timestamp only. + // "It works", but it's not the best solution. It does not accept "Date" as a parameter, but it works. subscription.roomUpdatedAt = Math.max(updatedAt, lastMessageTs); } else { // https://github.com/RocketChat/Rocket.Chat/blob/develop/app/ui-sidenav/client/roomList.js#L180 const lastRoomUpdate = room.lm || subscription.ts || subscription._updatedAt; + // @ts-ignore Same as above scenario subscription.roomUpdatedAt = subscription.lr - ? Math.max(new Date(subscription.lr), new Date(lastRoomUpdate)) + ? // @ts-ignore Same as above scenario + Math.max(new Date(subscription.lr), new Date(lastRoomUpdate)) : lastRoomUpdate; } subscription.ro = room.ro; @@ -76,7 +83,7 @@ export const merge = (subscription, room) => { } if (!subscription.name) { - subscription.name = subscription.fname; + subscription.name = subscription.fname as string; } if (!subscription.autoTranslate) { @@ -88,29 +95,24 @@ export const merge = (subscription, room) => { return subscription; }; -export default async (subscriptions = [], rooms = []) => { - if (subscriptions.update) { - subscriptions = subscriptions.update; - rooms = rooms.update; - } +export default async (serverSubscriptions: IServerSubscription, serverRooms: IServerRoom): Promise => { + const subscriptions = serverSubscriptions.update; + const rooms = serverRooms.update; // Find missing rooms/subscriptions on local database - ({ subscriptions, rooms } = await findSubscriptionsRooms(subscriptions, rooms)); + const findData = await findSubscriptionsRooms(subscriptions, rooms); // Merge each subscription into a room - subscriptions = subscriptions.map(s => { - const index = rooms.findIndex(({ _id }) => _id === s.rid); + const mergedSubscriptions = findData.subscriptions.map(subscription => { + const index = rooms.findIndex(({ _id }) => _id === subscription.rid); // Room not found if (index < 0) { - return merge(s); + return merge(subscription); } const [room] = rooms.splice(index, 1); - return merge(s, room); + return merge(subscription, room); }); // Decrypt all subscriptions missing decryption - subscriptions = await Encryption.decryptSubscriptions(subscriptions); + const decryptedSubscriptions = (await Encryption.decryptSubscriptions(mergedSubscriptions)) as ISubscription[]; - return { - subscriptions, - rooms - }; + return decryptedSubscriptions; }; diff --git a/app/lib/methods/loadMessagesForRoom.js b/app/lib/methods/loadMessagesForRoom.ts similarity index 62% rename from app/lib/methods/loadMessagesForRoom.js rename to app/lib/methods/loadMessagesForRoom.ts index 4a11bcc8c..db64a05ca 100644 --- a/app/lib/methods/loadMessagesForRoom.js +++ b/app/lib/methods/loadMessagesForRoom.ts @@ -5,32 +5,41 @@ import log from '../../utils/log'; import { getMessageById } from '../database/services/Message'; 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; -async function load({ rid: roomId, latest, t }) { - let params = { roomId, count: COUNT }; +async function load({ rid: roomId, latest, t }: { rid: string; latest?: string; t: RoomTypes }) { + let params = { roomId, count: COUNT } as { roomId: string; count: number; latest?: string }; if (latest) { params = { ...params, latest: new Date(latest).toISOString() }; } - const apiType = this.roomTypeToApiType(t); + const apiType = roomTypeToApiType(t); if (!apiType) { return []; } // RC 0.48.0 - const data = await this.sdk.get(`${apiType}.history`, params); + // @ts-ignore + const data: any = await sdk.get(`${apiType}.history`, params); if (!data || data.status === 'error') { return []; } return data.messages; } -export default function loadMessagesForRoom(args) { +export default function loadMessagesForRoom(args: { + rid: string; + t: RoomTypes; + latest: string; + loaderItem: TMessageModel; +}): Promise { return new Promise(async (resolve, reject) => { try { - const data = await load.call(this, args); + const data = await load(args); if (data?.length) { const lastMessage = data[data.length - 1]; const lastMessageRecord = await getMessageById(lastMessage._id); @@ -46,9 +55,8 @@ export default function loadMessagesForRoom(args) { } await updateMessages({ rid: args.rid, update: data, loaderItem: args.loaderItem }); return resolve(data); - } else { - return resolve([]); } + return resolve([]); } catch (e) { log(e); reject(e); diff --git a/app/lib/methods/loadMissedMessages.js b/app/lib/methods/loadMissedMessages.js deleted file mode 100644 index 7b2e8c978..000000000 --- a/app/lib/methods/loadMissedMessages.js +++ /dev/null @@ -1,44 +0,0 @@ -import database from '../database'; -import log from '../../utils/log'; -import updateMessages from './updateMessages'; - -const getLastUpdate = async rid => { - try { - const db = database.active; - const subsCollection = db.get('subscriptions'); - const sub = await subsCollection.find(rid); - return sub.lastOpen.toISOString(); - } catch (e) { - // Do nothing - } - return null; -}; - -async function load({ rid: roomId, lastOpen }) { - let lastUpdate; - if (lastOpen) { - lastUpdate = new Date(lastOpen).toISOString(); - } else { - lastUpdate = await getLastUpdate(roomId); - } - // RC 0.60.0 - const { result } = await this.sdk.get('chat.syncMessages', { roomId, lastUpdate }); - return result; -} - -export default function loadMissedMessages(args) { - return new Promise(async (resolve, reject) => { - try { - const data = await load.call(this, { rid: args.rid, lastOpen: args.lastOpen }); - - if (data) { - const { updated, deleted } = data; - await updateMessages({ rid: args.rid, update: updated, remove: deleted }); - } - resolve(); - } catch (e) { - log(e); - reject(e); - } - }); -} diff --git a/app/lib/methods/loadMissedMessages.ts b/app/lib/methods/loadMissedMessages.ts new file mode 100644 index 000000000..e4578a867 --- /dev/null +++ b/app/lib/methods/loadMissedMessages.ts @@ -0,0 +1,47 @@ +import { ILastMessage } from '../../definitions'; +import log from '../../utils/log'; +import database from '../database'; +import sdk from '../rocketchat/services/sdk'; +import updateMessages from './updateMessages'; + +const getLastUpdate = async (rid: string) => { + try { + const db = database.active; + const subsCollection = db.get('subscriptions'); + const sub = await subsCollection.find(rid); + return sub.lastOpen?.toISOString(); + } catch (e) { + // Do nothing + } + return null; +}; + +async function load({ rid: roomId, lastOpen }: { rid: string; lastOpen: string }) { + let lastUpdate; + if (lastOpen) { + lastUpdate = new Date(lastOpen).toISOString(); + } else { + lastUpdate = await getLastUpdate(roomId); + } + // RC 0.60.0 + // @ts-ignore // this method dont have type + const { result } = await sdk.get('chat.syncMessages', { roomId, lastUpdate }); + return result; +} + +export default function loadMissedMessages(args: { rid: string; lastOpen: string }): Promise { + return new Promise(async (resolve, reject) => { + try { + const data = await load({ rid: args.rid, lastOpen: args.lastOpen }); + if (data) { + const { updated, deleted }: { updated: ILastMessage[]; deleted: ILastMessage[] } = data; + // @ts-ignore // TODO: remove loaderItem obligatoriness + await updateMessages({ rid: args.rid, update: updated, remove: deleted }); + } + resolve(); + } catch (e) { + log(e); + reject(e); + } + }); +} diff --git a/app/lib/methods/loadNextMessages.js b/app/lib/methods/loadNextMessages.ts similarity index 74% rename from app/lib/methods/loadNextMessages.js rename to app/lib/methods/loadNextMessages.ts index 3fe728512..74f91d1f2 100644 --- a/app/lib/methods/loadNextMessages.js +++ b/app/lib/methods/loadNextMessages.ts @@ -7,13 +7,22 @@ import { getMessageById } from '../database/services/Message'; import { MESSAGE_TYPE_LOAD_NEXT_CHUNK } from '../../constants/messageTypeLoad'; import { generateLoadMoreId } from '../utils'; import updateMessages from './updateMessages'; +import { IMessage, TMessageModel } from '../../definitions'; +import RocketChat from '../rocketchat'; const COUNT = 50; -export default function loadNextMessages(args) { +interface ILoadNextMessages { + rid: string; + ts: string; + tmid: string; + loaderItem: TMessageModel; +} + +export default function loadNextMessages(args: ILoadNextMessages): Promise { return new Promise(async (resolve, reject) => { try { - const data = await this.methodCallWrapper('loadNextMessages', args.rid, args.ts, COUNT); + const data = await RocketChat.methodCallWrapper('loadNextMessages', args.rid, args.ts, COUNT); let messages = EJSON.fromJSONValue(data?.messages); messages = orderBy(messages, 'ts'); if (messages?.length) { @@ -31,9 +40,8 @@ export default function loadNextMessages(args) { } await updateMessages({ rid: args.rid, update: messages, loaderItem: args.loaderItem }); return resolve(messages); - } else { - return resolve([]); } + return resolve([]); } catch (e) { log(e); reject(e); diff --git a/app/lib/methods/sendFileMessage.js b/app/lib/methods/sendFileMessage.ts similarity index 71% rename from app/lib/methods/sendFileMessage.js rename to app/lib/methods/sendFileMessage.ts index 35d2815ca..cc11606b6 100644 --- a/app/lib/methods/sendFileMessage.js +++ b/app/lib/methods/sendFileMessage.ts @@ -1,18 +1,22 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { settings as RocketChatSettings } from '@rocket.chat/sdk'; +import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; +import isEmpty from 'lodash/isEmpty'; import FileUpload from '../../utils/fileUpload'; import database from '../database'; import log from '../../utils/log'; +import { IUpload, IUser, TUploadModel } from '../../definitions'; +import { IFileUpload } from '../../utils/fileUpload/interfaces'; -const uploadQueue = {}; +const uploadQueue: { [index: string]: StatefulPromise } = {}; -export function isUploadActive(path) { +export function isUploadActive(path: string): boolean { return !!uploadQueue[path]; } -export async function cancelUpload(item) { - if (uploadQueue[item.path]) { +export async function cancelUpload(item: TUploadModel): Promise { + if (!isEmpty(uploadQueue[item.path])) { try { await uploadQueue[item.path].cancel(); } catch { @@ -20,7 +24,7 @@ export async function cancelUpload(item) { } try { const db = database.active; - await db.action(async () => { + await db.write(async () => { await item.destroyPermanently(); }); } catch (e) { @@ -30,7 +34,13 @@ export async function cancelUpload(item) { } } -export function sendFileMessage(rid, fileInfo, tmid, server, user) { +export function sendFileMessage( + rid: string, + fileInfo: IUpload, + tmid: string, + server: string, + user: IUser +): Promise { return new Promise(async (resolve, reject) => { try { const { id, token } = user; @@ -41,16 +51,18 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { const db = database.active; const uploadsCollection = db.get('uploads'); - let uploadRecord; + let uploadRecord: TUploadModel; try { uploadRecord = await uploadsCollection.find(fileInfo.path); } catch (error) { try { - await db.action(async () => { + await db.write(async () => { uploadRecord = await uploadsCollection.create(u => { u._raw = sanitizedRaw({ id: fileInfo.path }, uploadsCollection.schema); Object.assign(u, fileInfo); - u.subscription.id = rid; + if (u.subscription) { + u.subscription.id = rid; + } }); }); } catch (e) { @@ -58,7 +70,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { } } - const formData = []; + const formData: IFileUpload[] = []; formData.push({ name: 'file', type: fileInfo.type, @@ -89,9 +101,9 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { uploadQueue[fileInfo.path] = FileUpload.fetch('POST', uploadUrl, headers, formData); - uploadQueue[fileInfo.path].uploadProgress(async (loaded, total) => { + uploadQueue[fileInfo.path].uploadProgress(async (loaded: number, total: number) => { try { - await db.action(async () => { + await db.write(async () => { await uploadRecord.update(u => { u.progress = Math.floor((loaded / total) * 100); }); @@ -105,7 +117,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { if (response.respInfo.status >= 200 && response.respInfo.status < 400) { // If response is all good... try { - await db.action(async () => { + await db.write(async () => { await uploadRecord.destroyPermanently(); }); resolve(response); @@ -114,7 +126,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { } } else { try { - await db.action(async () => { + await db.write(async () => { await uploadRecord.update(u => { u.error = true; }); @@ -132,7 +144,7 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) { uploadQueue[fileInfo.path].catch(async error => { try { - await db.action(async () => { + await db.write(async () => { await uploadRecord.update(u => { u.error = true; }); diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.ts similarity index 76% rename from app/lib/methods/sendMessage.js rename to app/lib/methods/sendMessage.ts index 1a1ff9934..b4bd9bf9e 100644 --- a/app/lib/methods/sendMessage.js +++ b/app/lib/methods/sendMessage.ts @@ -1,4 +1,5 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; +import { Model } from '@nozbe/watermelondb'; import messagesStatus from '../../constants/messagesStatus'; import database from '../database'; @@ -6,12 +7,14 @@ import log from '../../utils/log'; import random from '../../utils/random'; import { Encryption } from '../encryption'; import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../encryption/constants'; +import { IMessage, IUser, TMessageModel } from '../../definitions'; +import sdk from '../rocketchat/services/sdk'; -const changeMessageStatus = async (id, tmid, status, message) => { +const changeMessageStatus = async (id: string, status: number, tmid?: string, message?: IMessage) => { const db = database.active; const msgCollection = db.get('messages'); const threadMessagesCollection = db.get('thread_messages'); - const successBatch = []; + const successBatch: Model[] = []; const messageRecord = await msgCollection.find(id); successBatch.push( messageRecord.prepareUpdate(m => { @@ -37,7 +40,7 @@ const changeMessageStatus = async (id, tmid, status, message) => { } try { - await db.action(async () => { + await db.write(async () => { await db.batch(...successBatch); }); } catch (error) { @@ -45,48 +48,44 @@ const changeMessageStatus = async (id, tmid, status, message) => { } }; -export async function sendMessageCall(message) { +export async function sendMessageCall(message: any) { const { _id, tmid } = message; try { - const sdk = this.shareSDK || this.sdk; // RC 0.60.0 + // @ts-ignore const result = await sdk.post('chat.sendMessage', { message }); if (result.success) { - return changeMessageStatus(_id, tmid, messagesStatus.SENT, result.message); + // @ts-ignore + return changeMessageStatus(_id, messagesStatus.SENT, tmid, result.message); } } catch { // do nothing } - return changeMessageStatus(_id, tmid, messagesStatus.ERROR); + return changeMessageStatus(_id, messagesStatus.ERROR, tmid); } -export async function resendMessage(message, tmid) { +export async function resendMessage(message: TMessageModel, tmid?: string) { const db = database.active; try { - await db.action(async () => { + await db.write(async () => { await message.update(m => { m.status = messagesStatus.TEMP; }); }); - let m = { + const m = await Encryption.encryptMessage({ _id: message.id, - rid: message.subscription.id, - msg: message.msg - }; - if (tmid) { - m = { - ...m, - tmid - }; - } - m = await Encryption.encryptMessage(m); - await sendMessageCall.call(this, m); + rid: message.subscription ? message.subscription.id : '', + msg: message.msg, + ...(tmid && { tmid }) + } as IMessage); + + await sendMessageCall(m); } catch (e) { log(e); } } -export default async function (rid, msg, tmid, user, tshow) { +export default async function (rid: string, msg: string, tmid: string, user: IUser, tshow?: boolean) { try { const db = database.active; const subsCollection = db.get('subscriptions'); @@ -94,19 +93,18 @@ export default async function (rid, msg, tmid, user, tshow) { const threadCollection = db.get('threads'); const threadMessagesCollection = db.get('thread_messages'); const messageId = random(17); - const batch = []; + const batch: Model[] = []; - let message = { + const message = await Encryption.encryptMessage({ _id: messageId, rid, msg, tmid, tshow - }; - message = await Encryption.encryptMessage(message); + } as IMessage); const messageDate = new Date(); - let tMessageRecord; + let tMessageRecord: TMessageModel; // If it's replying to a thread if (tmid) { @@ -116,7 +114,9 @@ export default async function (rid, msg, tmid, user, tshow) { batch.push( tMessageRecord.prepareUpdate(m => { m.tlm = messageDate; - m.tcount += 1; + if (m.tcount) { + m.tcount += 1; + } }) ); @@ -128,7 +128,9 @@ export default async function (rid, msg, tmid, user, tshow) { batch.push( threadCollection.prepareCreate(tm => { tm._raw = sanitizedRaw({ id: tmid }, threadCollection.schema); - tm.subscription.id = rid; + if (tm.subscription) { + tm.subscription.id = rid; + } tm.tmid = tmid; tm.msg = tMessageRecord.msg; tm.ts = tMessageRecord.ts; @@ -147,7 +149,9 @@ export default async function (rid, msg, tmid, user, tshow) { batch.push( threadMessagesCollection.prepareCreate(tm => { tm._raw = sanitizedRaw({ id: messageId }, threadMessagesCollection.schema); - tm.subscription.id = rid; + if (tm.subscription) { + tm.subscription.id = rid; + } tm.rid = tmid; tm.msg = msg; tm.ts = messageDate; @@ -173,7 +177,9 @@ export default async function (rid, msg, tmid, user, tshow) { batch.push( msgCollection.prepareCreate(m => { m._raw = sanitizedRaw({ id: messageId }, msgCollection.schema); - m.subscription.id = rid; + if (m.subscription) { + m.subscription.id = rid; + } m.msg = msg; m.ts = messageDate; m._updatedAt = messageDate; @@ -210,7 +216,7 @@ export default async function (rid, msg, tmid, user, tshow) { } try { - await db.action(async () => { + await db.write(async () => { await db.batch(...batch); }); } catch (e) { @@ -218,7 +224,7 @@ export default async function (rid, msg, tmid, user, tshow) { return; } - await sendMessageCall.call(this, message); + await sendMessageCall(message); } catch (e) { log(e); } diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index 9e1cf8519..14cf75d5c 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -127,8 +127,11 @@ const createOrUpdateSubscription = async (subscription, room) => { } } - let tmp = merge(subscription, room); - tmp = await Encryption.decryptSubscription(tmp); + let tmp; + if (subscription) { + tmp = merge(subscription, room); + tmp = await Encryption.decryptSubscription(tmp); + } let sub; try { sub = await subCollection.find(tmp.rid); diff --git a/app/lib/methods/updateMessages.ts b/app/lib/methods/updateMessages.ts index ddfd87221..1e86d7997 100644 --- a/app/lib/methods/updateMessages.ts +++ b/app/lib/methods/updateMessages.ts @@ -13,7 +13,7 @@ import { getSubscriptionByRoomId } from '../database/services/Subscription'; interface IUpdateMessages { rid: string; update: IMessage[]; - remove: IMessage[]; + remove?: IMessage[]; loaderItem?: TMessageModel; } @@ -105,7 +105,7 @@ export default async function updateMessages({ threadCollection.prepareCreate( protectedFunction((t: TThreadModel) => { t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema); - t.subscription.id = sub.id; + if (t.subscription) t.subscription.id = sub.id; Object.assign(t, thread); }) ) diff --git a/app/lib/rocketchat/services/getServerTimeSync.ts b/app/lib/rocketchat/services/getServerTimeSync.ts new file mode 100644 index 000000000..da50c07ed --- /dev/null +++ b/app/lib/rocketchat/services/getServerTimeSync.ts @@ -0,0 +1,16 @@ +import RNFetchBlob from 'rn-fetch-blob'; + +export const getServerTimeSync = async (server: string) => { + try { + const response = await Promise.race([ + RNFetchBlob.fetch('GET', `${server}/_timesync`), + new Promise(res => setTimeout(res, 2000)) + ]); + if (response?.data) { + return parseInt(response.data); + } + return null; + } catch { + return null; + } +}; diff --git a/app/lib/rocketchat/services/restApi.ts b/app/lib/rocketchat/services/restApi.ts index 3fc518123..0ccf22034 100644 --- a/app/lib/rocketchat/services/restApi.ts +++ b/app/lib/rocketchat/services/restApi.ts @@ -34,10 +34,8 @@ export const createChannel = ({ return sdk.post(type ? 'groups.create' : 'channels.create', params); }; -export const e2eSetUserPublicAndPrivateKeys = (public_key: string, private_key: string): any => +export const e2eSetUserPublicAndPrivateKeys = (public_key: string, private_key: string) => // RC 2.2.0 - // TODO: missing definitions from server - // @ts-ignore sdk.post('e2e.setUserPublicAndPrivateKeys', { public_key, private_key }); export const e2eRequestSubscriptionKeys = (): any => @@ -221,12 +219,6 @@ export const teamListRoomsOfUser = ({ teamId, userId }: { teamId: string; userId // @ts-ignore sdk.get('teams.listRoomsOfUser', { teamId, userId }); -export const getTeamInfo = ({ teamId }: { teamId: string }): any => - // RC 3.13.0 - // TODO: missing definitions from server - // @ts-ignore - sdk.get('teams.info', { teamId }); - export const convertChannelToTeam = ({ rid, name, type }: { rid: string; name: string; type: 'c' | 'p' }): any => { const params = { ...(type === 'c' diff --git a/app/lib/utils.ts b/app/lib/utils.ts index bece818b2..3a0e58e12 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -18,7 +18,7 @@ const methods = { }; export const compareServerVersion = ( - currentServerVersion: string, + currentServerVersion: string | null, method: keyof typeof methods, versionToCompare: string ): boolean => diff --git a/app/reducers/activeUsers.test.ts b/app/reducers/activeUsers.test.ts index b78db6db4..42a10d402 100644 --- a/app/reducers/activeUsers.test.ts +++ b/app/reducers/activeUsers.test.ts @@ -1,4 +1,5 @@ import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers'; +import { UserStatus } from '../definitions/UserStatus'; import { IActiveUsers, initialState } from './activeUsers'; import { mockedStore } from './mockedStore'; @@ -8,7 +9,7 @@ describe('test reducer', () => { expect(state).toEqual(initialState); }); it('should return modified store after action', () => { - const activeUsers: IActiveUsers = { any: { status: 'online', statusText: 'any' } }; + const activeUsers: IActiveUsers = { any: { status: UserStatus.ONLINE, statusText: 'any' } }; mockedStore.dispatch(setActiveUsers(activeUsers)); const state = mockedStore.getState().activeUsers; expect(state).toEqual({ ...activeUsers }); diff --git a/app/reducers/activeUsers.ts b/app/reducers/activeUsers.ts index bfd765654..34a55ce6d 100644 --- a/app/reducers/activeUsers.ts +++ b/app/reducers/activeUsers.ts @@ -1,9 +1,9 @@ -import { TApplicationActions } from '../definitions'; import { ACTIVE_USERS } from '../actions/actionsTypes'; +import { TApplicationActions } from '../definitions'; +import { UserStatus } from '../definitions/UserStatus'; -type TUserStatus = 'online' | 'offline' | 'away' | 'busy'; export interface IActiveUser { - status: TUserStatus; + status: UserStatus; statusText: string; } diff --git a/app/reducers/enterpriseModules.ts b/app/reducers/enterpriseModules.ts index 78616e246..fbd9fd52f 100644 --- a/app/reducers/enterpriseModules.ts +++ b/app/reducers/enterpriseModules.ts @@ -1,7 +1,7 @@ import { TActionEnterpriseModules } from '../actions/enterpriseModules'; import { ENTERPRISE_MODULES } from '../actions/actionsTypes'; -export type IEnterpriseModules = 'omnichannel-mobile-enterprise' | 'livechat-enterprise'; +export type IEnterpriseModules = string; export const initialState: IEnterpriseModules[] = []; diff --git a/app/reducers/login.test.ts b/app/reducers/login.test.ts new file mode 100644 index 000000000..abab2ebaf --- /dev/null +++ b/app/reducers/login.test.ts @@ -0,0 +1,109 @@ +import { + loginFailure, + loginRequest, + loginSuccess, + logout, + setLocalAuthenticated, + setLoginServices, + setUser +} from '../actions/login'; +import { UserStatus } from '../definitions/UserStatus'; +import { initialState } from './login'; +import { mockedStore } from './mockedStore'; + +describe('test selectedUsers reducer', () => { + it('should return initial state', () => { + const state = mockedStore.getState().login; + expect(state).toEqual(initialState); + }); + + it('should return modified store after loginRequest', () => { + mockedStore.dispatch(loginRequest({ user: 'carlitos@email.com', password: '123456' })); + const state = mockedStore.getState().login; + expect(state).toEqual({ ...initialState, isFetching: true, isAuthenticated: false, failure: false, error: {} }); + }); + + it('should return modified store after loginFailure', () => { + mockedStore.dispatch(loginFailure({ error: 'error' })); + const state = mockedStore.getState().login.error.error; + expect(state).toEqual('error'); + }); + + it('should return modified store after loginSuccess', () => { + const user = { + id: 'ajhsiahsa', + token: 'asdasdasdas', + username: 'carlitos', + name: 'Carlitos', + customFields: { + phonenumber: '' + }, + emails: [ + { + address: 'carlitos@email.com', + verified: true + } + ], + roles: ['user'], + isFromWebView: false, + showMessageInMainThread: false, + enableMessageParserEarlyAdoption: false, + status: UserStatus.ONLINE, + statusText: 'online' + }; + mockedStore.dispatch(loginSuccess(user)); + const state = mockedStore.getState().login.user; + expect(state).toEqual(user); + }); + + it('should return modified store after setUser', () => { + const user = { + id: 'ajhsiahsa', + token: 'asdasdasdas', + username: 'carlito', + name: 'Carlitos', + customFields: { + phonenumber: '' + }, + emails: [ + { + address: 'carlitos@email.com', + verified: true + } + ], + roles: ['user'], + isFromWebView: false, + showMessageInMainThread: false, + enableMessageParserEarlyAdoption: false + }; + mockedStore.dispatch(setUser(user)); + const state = mockedStore.getState().login.user.username; + expect(state).toEqual(user.username); + }); + + // TODO PREFERENCE REDUCER WITH EMPTY PREFERENCE - NON USED? + // it('should return modified store after setPreference', () => { + // mockedStore.dispatch(setPreference({ showAvatar: true })); + // const state = mockedStore.getState().login; + // console.log(state); + // expect(state).toEqual('error'); + // }); + + it('should return modified store after setLocalAuthenticated', () => { + mockedStore.dispatch(setLocalAuthenticated(true)); + const state = mockedStore.getState().login.isLocalAuthenticated; + expect(state).toEqual(true); + }); + + it('should return modified store after setLoginServices', () => { + mockedStore.dispatch(setLoginServices({ facebook: { clientId: 'xxx' } })); + const state = mockedStore.getState().login.services.facebook.clientId; + expect(state).toEqual('xxx'); + }); + + it('should return modified store after logout', () => { + mockedStore.dispatch(logout()); + const state = mockedStore.getState().login; + expect(state).toEqual(initialState); + }); +}); diff --git a/app/reducers/login.js b/app/reducers/login.ts similarity index 50% rename from app/reducers/login.js rename to app/reducers/login.ts index 664718c4c..2969d0c28 100644 --- a/app/reducers/login.js +++ b/app/reducers/login.ts @@ -1,15 +1,47 @@ +import { UserStatus } from '../definitions/UserStatus'; import * as types from '../actions/actionsTypes'; +import { TActionsLogin } from '../actions/login'; +import { IUser } from '../definitions'; -const initialState = { +export interface IUserLogin { + id: string; + token: string; + username: string; + name: string; + language?: string; + status: UserStatus; + statusText: string; + roles: string[]; + avatarETag?: string; + isFromWebView: boolean; + showMessageInMainThread: boolean; + enableMessageParserEarlyAdoption: boolean; + emails: Record[]; + customFields: Record; + settings?: Record; +} + +export interface ILogin { + user: Partial; + isLocalAuthenticated: boolean; + isAuthenticated: boolean; + isFetching: boolean; + error: Record; + services: Record; + failure: boolean; +} + +export const initialState: ILogin = { isLocalAuthenticated: true, isAuthenticated: false, isFetching: false, user: {}, error: {}, - services: {} + services: {}, + failure: false }; -export default function login(state = initialState, action) { +export default function login(state = initialState, action: TActionsLogin): ILogin { switch (action.type) { case types.APP.INIT: return initialState; @@ -60,13 +92,14 @@ export default function login(state = initialState, action) { ...state, user: { ...state.user, - settings: { - ...state.user.settings, - preferences: { - ...state.user.settings.preferences, - ...action.preference - } - } + settings: state.user?.settings + ? { + ...state.user?.settings, + preferences: state.user?.settings?.preferences + ? { ...state.user.settings.preferences, ...action.preference } + : { ...action.preference } + } + : { profile: {}, preferences: {} } } }; case types.LOGIN.SET_LOCAL_AUTHENTICATED: diff --git a/app/reducers/room.test.ts b/app/reducers/room.test.ts new file mode 100644 index 000000000..7ffba8e2a --- /dev/null +++ b/app/reducers/room.test.ts @@ -0,0 +1,58 @@ +import { closeRoom, deleteRoom, forwardRoom, leaveRoom, removedRoom, subscribeRoom, unsubscribeRoom } from '../actions/room'; +import { ERoomType } from '../definitions/ERoomType'; +import { mockedStore } from './mockedStore'; +import { initialState } from './room'; + +describe('test room reducer', () => { + it('should return initial state', () => { + const state = mockedStore.getState().room; + expect(state).toEqual(initialState); + }); + + it('should return modified store after subscribeRoom', () => { + mockedStore.dispatch(subscribeRoom('GENERAL')); + const state = mockedStore.getState().room; + expect(state.rooms).toEqual(['GENERAL']); + }); + + it('should return empty store after remove unsubscribeRoom', () => { + mockedStore.dispatch(unsubscribeRoom('GENERAL')); + const state = mockedStore.getState().room; + expect(state.rooms).toEqual([]); + }); + + it('should return initial state after leaveRoom', () => { + mockedStore.dispatch(leaveRoom(ERoomType.c, { rid: ERoomType.c })); + const { rid, isDeleting } = mockedStore.getState().room; + expect(rid).toEqual(ERoomType.c); + expect(isDeleting).toEqual(true); + }); + + it('should return initial state after deleteRoom', () => { + mockedStore.dispatch(deleteRoom(ERoomType.l, { rid: ERoomType.l })); + const { rid, isDeleting } = mockedStore.getState().room; + expect(rid).toEqual(ERoomType.l); + expect(isDeleting).toEqual(true); + }); + + it('should return initial state after closeRoom', () => { + mockedStore.dispatch(closeRoom('CLOSING')); + const { rid, isDeleting } = mockedStore.getState().room; + expect(rid).toEqual('CLOSING'); + expect(isDeleting).toEqual(true); + }); + + it('should return initial state after forwardRoom', () => { + const transferData = { roomId: 'FORWARDING' }; + mockedStore.dispatch(forwardRoom('FORWARDING', transferData)); + const { rid, isDeleting } = mockedStore.getState().room; + expect(rid).toEqual('FORWARDING'); + expect(isDeleting).toEqual(true); + }); + + it('should return loading after call removedRoom', () => { + mockedStore.dispatch(removedRoom()); + const { isDeleting } = mockedStore.getState().room; + expect(isDeleting).toEqual(false); + }); +}); diff --git a/app/reducers/room.js b/app/reducers/room.ts similarity index 73% rename from app/reducers/room.js rename to app/reducers/room.ts index 086e43f5c..e5b9815b4 100644 --- a/app/reducers/room.js +++ b/app/reducers/room.ts @@ -1,12 +1,21 @@ +import { TActionsRoom } from '../actions/room'; import { ROOM } from '../actions/actionsTypes'; -const initialState = { - rid: null, +export type IRoomRecord = string[]; + +export interface IRoom { + rid: string; + isDeleting: boolean; + rooms: IRoomRecord; +} + +export const initialState: IRoom = { + rid: '', isDeleting: false, rooms: [] }; -export default function (state = initialState, action) { +export default function (state = initialState, action: TActionsRoom): IRoom { switch (action.type) { case ROOM.SUBSCRIBE: return { diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js index 56d6c6c42..9102be9f3 100644 --- a/app/sagas/rooms.js +++ b/app/sagas/rooms.js @@ -57,8 +57,7 @@ const handleRoomsRequest = function* handleRoomsRequest({ params }) { } const [subscriptionsResult, roomsResult] = yield RocketChat.getRooms(roomsUpdatedAt); - const { subscriptions } = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult); - + const subscriptions = yield mergeSubscriptionsRooms(subscriptionsResult, roomsResult); const db = database.active; const subCollection = db.get('subscriptions'); const messagesCollection = db.get('messages'); diff --git a/app/selectors/login.ts b/app/selectors/login.ts index d9524b6e4..2b634d6ab 100644 --- a/app/selectors/login.ts +++ b/app/selectors/login.ts @@ -1,7 +1,7 @@ import { createSelector } from 'reselect'; import isEmpty from 'lodash/isEmpty'; -import { IApplicationState } from '../definitions'; +import { IApplicationState, IUser } from '../definitions'; interface IServices { facebook: { clientId: string }; @@ -13,7 +13,7 @@ interface IServices { wordpress: { clientId: string; serverURL: string }; } -const getUser = (state: IApplicationState) => { +const getUser = (state: IApplicationState): Partial => { if (!isEmpty(state.share?.user)) { return state.share.user; } @@ -23,7 +23,8 @@ const getLoginServices = (state: IApplicationState) => (state.login.services as const getShowFormLoginSetting = (state: IApplicationState) => (state.settings.Accounts_ShowFormLogin as boolean) || false; const getIframeEnabledSetting = (state: IApplicationState) => (state.settings.Accounts_iframe_enabled as boolean) || false; -export const getUserSelector = createSelector([getUser], user => user); +// TODO: we need to change 42 files to fix a correct type, i believe is better to do this later +export const getUserSelector = createSelector([getUser], user => user) as any; export const getShowLoginButton = createSelector( [getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting], diff --git a/app/utils/fileUpload/index.ios.ts b/app/utils/fileUpload/index.ios.ts index ae5cfabc2..96c2ae355 100644 --- a/app/utils/fileUpload/index.ios.ts +++ b/app/utils/fileUpload/index.ios.ts @@ -43,6 +43,7 @@ class FileUpload { upload.formData.append(item.name, { // @ts-ignore uri: item.uri, + // @ts-ignore type: item.type, name: item.filename }); diff --git a/app/utils/fileUpload/interfaces.ts b/app/utils/fileUpload/interfaces.ts index a3002f727..91b0d7d46 100644 --- a/app/utils/fileUpload/interfaces.ts +++ b/app/utils/fileUpload/interfaces.ts @@ -1,7 +1,7 @@ export interface IFileUpload { name: string; uri?: string; - type: string; - filename: string; - data: any; + type?: string; + filename?: string; + data?: any; } diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index b3b51ba8a..141268b4d 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -1,12 +1,13 @@ import * as LocalAuthentication from 'expo-local-authentication'; -import moment from 'moment'; import RNBootSplash from 'react-native-bootsplash'; import AsyncStorage from '@react-native-community/async-storage'; import { sha256 } from 'js-sha256'; +import moment from 'moment'; import UserPreferences from '../lib/userPreferences'; import { store } from '../lib/auxStore'; import database from '../lib/database'; +import { getServerTimeSync } from '../lib/rocketchat/services/getServerTimeSync'; import { ATTEMPTS_KEY, BIOMETRY_ENABLED_KEY, @@ -21,16 +22,25 @@ import { TServerModel } from '../definitions/IServer'; import EventEmitter from './events'; import { isIOS } from './deviceInfo'; -export const saveLastLocalAuthenticationSession = async (server: string, serverRecord?: TServerModel): Promise => { +export const saveLastLocalAuthenticationSession = async ( + server: string, + serverRecord?: TServerModel, + timesync?: number | null +): Promise => { + if (!timesync) { + timesync = new Date().getTime(); + } + const serversDB = database.servers; const serversCollection = serversDB.get('servers'); await serversDB.write(async () => { try { if (!serverRecord) { - serverRecord = (await serversCollection.find(server)) as TServerModel; + serverRecord = await serversCollection.find(server); } + const time = timesync || 0; await serverRecord.update(record => { - record.lastLocalAuthenticatedSession = new Date(); + record.lastLocalAuthenticatedSession = new Date(time); }); } catch (e) { // Do nothing @@ -103,6 +113,9 @@ export const localAuthenticate = async (server: string): Promise => { // if screen lock is enabled if (serverRecord?.autoLock) { + // Get time from server + const timesync = await getServerTimeSync(server); + // Make sure splash screen has been hidden try { await RNBootSplash.hide(); @@ -116,10 +129,10 @@ export const localAuthenticate = async (server: string): Promise => { // `checkHasPasscode` results newPasscode = true if a passcode has been set if (!result?.newPasscode) { // diff to last authenticated session - const diffToLastSession = moment().diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); + const diffToLastSession = moment(timesync).diff(serverRecord?.lastLocalAuthenticatedSession, 'seconds'); - // if last authenticated session is older than configured auto lock time, authentication is required - if (diffToLastSession >= serverRecord.autoLockTime!) { + // if it was not possible to get `timesync` from server or the last authenticated session is older than the configured auto lock time, authentication is required + if (!timesync || (serverRecord?.autoLockTime && diffToLastSession >= serverRecord.autoLockTime)) { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); @@ -141,7 +154,7 @@ export const localAuthenticate = async (server: string): Promise => { } await resetAttempts(); - await saveLastLocalAuthenticationSession(server, serverRecord); + await saveLastLocalAuthenticationSession(server, serverRecord, timesync); } }; diff --git a/app/utils/messageTypes.ts b/app/utils/messageTypes.ts index 4eefa614f..436856346 100644 --- a/app/utils/messageTypes.ts +++ b/app/utils/messageTypes.ts @@ -58,5 +58,33 @@ export const MessageTypeValues = [ { value: 'room_unarchived', text: 'Message_HideType_room_unarchived' + }, + { + value: 'removed-user-from-team', + text: 'Message_HideType_removed_user_from_team' + }, + { + value: 'added-user-to-team', + text: 'Message_HideType_added_user_to_team' + }, + { + value: 'user-added-room-to-team', + text: 'Message_HideType_user_added_room_to_team' + }, + { + value: 'user-converted-to-channel', + text: 'Message_HideType_user_converted_to_channel' + }, + { + value: 'user-converted-to-team', + text: 'Message_HideType_user_converted_to_team' + }, + { + value: 'user-deleted-room-from-team', + text: 'Message_HideType_user_deleted_room_from_team' + }, + { + value: 'user-removed-room-from-team', + text: 'Message_HideType_user_removed_room_from_team' } ]; diff --git a/app/utils/room.ts b/app/utils/room.ts index d39e40490..4bb8a29b9 100644 --- a/app/utils/room.ts +++ b/app/utils/room.ts @@ -22,7 +22,7 @@ export const capitalize = (s: string): string => { return s.charAt(0).toUpperCase() + s.slice(1); }; -export const formatDate = (date: Date): string => +export const formatDate = (date: string | Date): string => moment(date).calendar(null, { lastDay: `[${I18n.t('Yesterday')}]`, sameDay: 'LT', @@ -30,7 +30,7 @@ export const formatDate = (date: Date): string => sameElse: 'L' }); -export const formatDateThreads = (date: Date): string => +export const formatDateThreads = (date: string | Date): string => moment(date).calendar(null, { sameDay: 'LT', lastDay: `[${I18n.t('Yesterday')}] LT`, diff --git a/app/views/CannedResponsesListView/index.tsx b/app/views/CannedResponsesListView/index.tsx index 1c0f4ed18..47ec73c65 100644 --- a/app/views/CannedResponsesListView/index.tsx +++ b/app/views/CannedResponsesListView/index.tsx @@ -260,7 +260,7 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView /> ), - headerTitle: () => , + headerTitle: () => , headerTitleContainerStyle: { left: headerTitlePosition.left, right: headerTitlePosition.right diff --git a/app/views/ForwardLivechatView.tsx b/app/views/ForwardLivechatView.tsx index ff0840750..21c99a0f6 100644 --- a/app/views/ForwardLivechatView.tsx +++ b/app/views/ForwardLivechatView.tsx @@ -1,20 +1,17 @@ -import React, { useEffect, useState } from 'react'; -import { StackNavigationProp } from '@react-navigation/stack'; -import { RouteProp } from '@react-navigation/native'; -import { StyleSheet, View } from 'react-native'; -import { Dispatch } from 'redux'; -import { connect } from 'react-redux'; import isEmpty from 'lodash/isEmpty'; +import React, { useEffect, useState } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { useDispatch } from 'react-redux'; -import I18n from '../i18n'; -import { withTheme } from '../theme'; +import { forwardRoom, ITransferData } from '../actions/room'; import { themes } from '../constants/colors'; -import RocketChat from '../lib/rocketchat'; import OrSeparator from '../containers/OrSeparator'; import Input from '../containers/UIKit/MultiSelect/Input'; -import { forwardRoom as forwardRoomAction } from '../actions/room'; -import { IRoom } from '../definitions'; +import { IBaseScreen, IRoom } from '../definitions'; +import I18n from '../i18n'; +import RocketChat from '../lib/rocketchat'; import { ChatsStackParamList } from '../stacks/types'; +import { withTheme } from '../theme'; import { IOptionsField } from './NotificationPreferencesView/options'; const styles = StyleSheet.create({ @@ -23,33 +20,26 @@ const styles = StyleSheet.create({ padding: 16 } }); - -interface ITransferData { - roomId: string; - userId?: string; - departmentId?: string; -} - interface IUser { username: string; _id: string; } -interface IForwardLivechatViewProps { - navigation: StackNavigationProp; - route: RouteProp; - theme: string; - forwardRoom: (rid: string, transferData: ITransferData) => void; + +interface IParsedData { + label: string; + value: string; } const COUNT_DEPARTMENT = 50; -const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForwardLivechatViewProps) => { - const [departments, setDepartments] = useState([]); +const ForwardLivechatView = ({ navigation, route, theme }: IBaseScreen) => { + const [departments, setDepartments] = useState([]); const [departmentId, setDepartment] = useState(''); const [departmentTotal, setDepartmentTotal] = useState(0); const [users, setUsers] = useState([]); const [userId, setUser] = useState(); const [room, setRoom] = useState({} as IRoom); + const dispatch = useDispatch(); const rid = route.params?.rid; @@ -57,7 +47,7 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward try { const result = await RocketChat.getDepartments({ count: COUNT_DEPARTMENT, text, offset }); if (result.success) { - const parsedDepartments: IOptionsField[] = result.departments.map(department => ({ + const parsedDepartments: IParsedData[] = result.departments.map(department => ({ label: department.name, value: department._id })); @@ -116,7 +106,7 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward transferData.departmentId = departmentId; } - forwardRoom(rid, transferData); + dispatch(forwardRoom(rid, transferData)); }; useEffect(() => { @@ -171,8 +161,4 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward ); }; -const mapDispatchToProps = (dispatch: Dispatch) => ({ - forwardRoom: (rid: string, transferData: ITransferData) => dispatch(forwardRoomAction(rid, transferData)) -}); - -export default connect(null, mapDispatchToProps)(withTheme(ForwardLivechatView)); +export default withTheme(ForwardLivechatView); diff --git a/app/views/LoginView.tsx b/app/views/LoginView.tsx index e43505f3f..a8bcba275 100644 --- a/app/views/LoginView.tsx +++ b/app/views/LoginView.tsx @@ -1,21 +1,20 @@ +import { dequal } from 'dequal'; import React from 'react'; import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native'; import { connect } from 'react-redux'; -import { dequal } from 'dequal'; -import { StackNavigationProp } from '@react-navigation/stack'; -import { RouteProp } from '@react-navigation/core'; -import Button from '../containers/Button'; -import I18n from '../i18n'; -import * as HeaderButton from '../containers/HeaderButton'; +import { loginRequest } from '../actions/login'; import { themes } from '../constants/colors'; -import { withTheme } from '../theme'; +import Button from '../containers/Button'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; -import TextInput from '../containers/TextInput'; -import { loginRequest as loginRequestAction } from '../actions/login'; +import * as HeaderButton from '../containers/HeaderButton'; import LoginServices from '../containers/LoginServices'; -import sharedStyles from './Styles'; +import TextInput from '../containers/TextInput'; +import { IApplicationState, IBaseScreen } from '../definitions'; +import I18n from '../i18n'; import { OutsideParamList } from '../stacks/types'; +import { withTheme } from '../theme'; +import sharedStyles from './Styles'; const styles = StyleSheet.create({ registerDisabled: { @@ -48,9 +47,7 @@ const styles = StyleSheet.create({ } }); -interface ILoginViewProps { - navigation: StackNavigationProp; - route: RouteProp; +interface ILoginViewProps extends IBaseScreen { Site_Name: string; Accounts_RegistrationForm: string; Accounts_RegistrationForm_LinkReplacementText: string; @@ -63,7 +60,6 @@ interface ILoginViewProps { error: string; }; failure: boolean; - theme: string; loginRequest: Function; inviteLinkToken: string; } @@ -132,9 +128,9 @@ class LoginView extends React.Component { } const { user, password } = this.state; - const { loginRequest } = this.props; + const { dispatch } = this.props; Keyboard.dismiss(); - loginRequest({ user, password }); + dispatch(loginRequest({ user, password })); }; renderUserForm = () => { @@ -243,23 +239,19 @@ class LoginView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, - Site_Name: state.settings.Site_Name, - Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin, - Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm, - Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText, + Site_Name: state.settings.Site_Name as string, + Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin as boolean, + Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm as string, + Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText as string, isFetching: state.login.isFetching, failure: state.login.failure, error: state.login.error && state.login.error.data, - Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, - Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, - Accounts_PasswordReset: state.settings.Accounts_PasswordReset, + Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder as string, + Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder as string, + Accounts_PasswordReset: state.settings.Accounts_PasswordReset as boolean, inviteLinkToken: state.inviteLinks.token }); -const mapDispatchToProps = (dispatch: any) => ({ - loginRequest: (params: any) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginView)); +export default connect(mapStateToProps)(withTheme(LoginView)); diff --git a/app/views/MessagesView/index.tsx b/app/views/MessagesView/index.tsx index 89cd966ad..0912ccf67 100644 --- a/app/views/MessagesView/index.tsx +++ b/app/views/MessagesView/index.tsx @@ -71,6 +71,8 @@ interface IMessageItem { msg?: string; starred: boolean; pinned: boolean; + type: string; + url: string; } interface IParams { diff --git a/app/views/RegisterView.tsx b/app/views/RegisterView.tsx index ae5f46bd9..a4f1d025d 100644 --- a/app/views/RegisterView.tsx +++ b/app/views/RegisterView.tsx @@ -1,26 +1,25 @@ import React from 'react'; import { Keyboard, StyleSheet, Text, View } from 'react-native'; -import { StackNavigationProp } from '@react-navigation/stack'; -import { RouteProp } from '@react-navigation/core'; -import { connect } from 'react-redux'; import RNPickerSelect from 'react-native-picker-select'; +import { connect } from 'react-redux'; -import { OutsideParamList } from '../stacks/types'; -import log, { events, logEvent } from '../utils/log'; -import Button from '../containers/Button'; -import I18n from '../i18n'; -import * as HeaderButton from '../containers/HeaderButton'; +import { loginRequest } from '../actions/login'; import { themes } from '../constants/colors'; -import { withTheme } from '../theme'; +import Button from '../containers/Button'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; -import TextInput from '../containers/TextInput'; -import isValidEmail from '../utils/isValidEmail'; -import { showErrorAlert } from '../utils/info'; -import RocketChat from '../lib/rocketchat'; -import { loginRequest as loginRequestAction } from '../actions/login'; -import openLink from '../utils/openLink'; +import * as HeaderButton from '../containers/HeaderButton'; import LoginServices from '../containers/LoginServices'; +import TextInput from '../containers/TextInput'; +import { IApplicationState, IBaseScreen } from '../definitions'; +import I18n from '../i18n'; +import RocketChat from '../lib/rocketchat'; import { getShowLoginButton } from '../selectors/login'; +import { OutsideParamList } from '../stacks/types'; +import { withTheme } from '../theme'; +import { showErrorAlert } from '../utils/info'; +import isValidEmail from '../utils/isValidEmail'; +import log, { events, logEvent } from '../utils/log'; +import openLink from '../utils/openLink'; import sharedStyles from './Styles'; const styles = StyleSheet.create({ @@ -51,9 +50,7 @@ const styles = StyleSheet.create({ } }); -interface IProps { - navigation: StackNavigationProp; - route: RouteProp; +interface IProps extends IBaseScreen { server: string; Site_Name: string; Gitlab_URL: string; @@ -63,8 +60,6 @@ interface IProps { Accounts_EmailVerification: boolean; Accounts_ManuallyApproveNewUsers: boolean; showLoginButton: boolean; - loginRequest: Function; - theme: string; } class RegisterView extends React.Component { @@ -130,7 +125,7 @@ class RegisterView extends React.Component { Keyboard.dismiss(); const { name, email, password, username, customFields } = this.state; - const { loginRequest, Accounts_EmailVerification, navigation, Accounts_ManuallyApproveNewUsers } = this.props; + const { dispatch, Accounts_EmailVerification, navigation, Accounts_ManuallyApproveNewUsers } = this.props; try { await RocketChat.register({ @@ -148,11 +143,11 @@ class RegisterView extends React.Component { await navigation.goBack(); showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded')); } else { - await loginRequest({ user: email, password }); + dispatch(loginRequest({ user: email, password })); } } catch (e: any) { if (e.data?.errorType === 'username-invalid') { - return loginRequest({ user: email, password }); + return dispatch(loginRequest({ user: email, password })); } if (e.data?.error) { logEvent(events.REGISTER_DEFAULT_SIGN_UP_F); @@ -349,20 +344,16 @@ class RegisterView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, - Site_Name: state.settings.Site_Name, - Gitlab_URL: state.settings.API_Gitlab_URL, - CAS_enabled: state.settings.CAS_enabled, - CAS_login_url: state.settings.CAS_login_url, - Accounts_CustomFields: state.settings.Accounts_CustomFields, - Accounts_EmailVerification: state.settings.Accounts_EmailVerification, - Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers, + Site_Name: state.settings.Site_Name as string, + Gitlab_URL: state.settings.API_Gitlab_URL as string, + CAS_enabled: state.settings.CAS_enabled as boolean, + CAS_login_url: state.settings.CAS_login_url as string, + Accounts_CustomFields: state.settings.Accounts_CustomFields as string, + Accounts_EmailVerification: state.settings.Accounts_EmailVerification as boolean, + Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers as boolean, showLoginButton: getShowLoginButton(state) }); -const mapDispatchToProps = (dispatch: any) => ({ - loginRequest: (params: any) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RegisterView)); +export default connect(mapStateToProps)(withTheme(RegisterView)); diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 44075868b..a9ede0ca3 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -7,8 +7,8 @@ import { Q } from '@nozbe/watermelondb'; import { compareServerVersion } from '../../lib/utils'; import Touch from '../../utils/touch'; -import { setLoading as setLoadingAction } from '../../actions/selectedUsers'; -import { closeRoom as closeRoomAction, leaveRoom as leaveRoomAction } from '../../actions/room'; +import { setLoading } from '../../actions/selectedUsers'; +import { closeRoom, leaveRoom } from '../../actions/room'; import sharedStyles from '../Styles'; import Avatar from '../../containers/Avatar'; import Status from '../../containers/Status'; @@ -334,9 +334,9 @@ class RoomActionsView extends React.Component { const { room: { rid } } = this.state; - const { closeRoom } = this.props; + const { dispatch } = this.props; - closeRoom(rid); + dispatch(closeRoom(rid)); }; returnLivechat = () => { @@ -375,16 +375,16 @@ class RoomActionsView extends React.Component { addUser = async () => { const { room } = this.state; - const { setLoadingInvite, navigation } = this.props; + const { dispatch, navigation } = this.props; const { rid } = room; try { - setLoadingInvite(true); + dispatch(setLoading(true)); await RocketChat.addUsersToRoom(rid); navigation.pop(); } catch (e) { log(e); } finally { - setLoadingInvite(false); + dispatch(setLoading(false)); } }; @@ -458,12 +458,12 @@ class RoomActionsView extends React.Component { leaveChannel = () => { const { room } = this.state; - const { leaveRoom } = this.props; + const { dispatch } = this.props; showConfirmationAlert({ message: I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }), confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }), - onPress: () => leaveRoom('channel', room) + onPress: () => dispatch(leaveRoom('channel', room)) }); }; @@ -522,7 +522,7 @@ class RoomActionsView extends React.Component { leaveTeam = async () => { const { room } = this.state; - const { navigation, leaveRoom } = this.props; + const { navigation, dispatch } = this.props; try { const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id }); @@ -538,21 +538,21 @@ class RoomActionsView extends React.Component { title: 'Leave_Team', data: teamChannels, infoText: 'Select_Team_Channels', - nextAction: data => leaveRoom('team', room, data), + nextAction: data => dispatch(leaveRoom('team', room, data)), showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_leave')) }); } else { showConfirmationAlert({ message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }), confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }), - onPress: () => leaveRoom('team', room) + onPress: () => dispatch(leaveRoom('team', room)) }); } } catch (e) { showConfirmationAlert({ message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }), confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }), - onPress: () => leaveRoom('team', room) + onPress: () => dispatch(leaveRoom('team', room)) }); } }; @@ -1242,10 +1242,4 @@ const mapStateToProps = state => ({ viewCannedResponsesPermission: state.permissions['view-canned-responses'] }); -const mapDispatchToProps = dispatch => ({ - leaveRoom: (roomType, room, selected) => dispatch(leaveRoomAction(roomType, room, selected)), - closeRoom: rid => dispatch(closeRoomAction(rid)), - setLoadingInvite: loading => dispatch(setLoadingAction(loading)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(withDimensions(RoomActionsView))); +export default connect(mapStateToProps)(withTheme(withDimensions(RoomActionsView))); diff --git a/app/views/SearchMessagesView/index.tsx b/app/views/SearchMessagesView/index.tsx index b1b8c1ed9..2da0c7a62 100644 --- a/app/views/SearchMessagesView/index.tsx +++ b/app/views/SearchMessagesView/index.tsx @@ -32,6 +32,7 @@ import { isIOS } from '../../utils/deviceInfo'; import { compareServerVersion } from '../../lib/utils'; import styles from './styles'; import { InsideStackParamList, ChatsStackParamList } from '../../stacks/types'; +import { IRoom } from '../../definitions'; import { IEmoji } from '../../definitions/IEmoji'; const QUERY_SIZE = 50; @@ -81,7 +82,7 @@ class SearchMessagesView extends React.Component | null | undefined; static navigationOptions = ({ navigation, route }: INavigationOption) => { const options: StackNavigationOptions = { diff --git a/app/views/SetUsernameView.tsx b/app/views/SetUsernameView.tsx index 221561697..4c9aa91f0 100644 --- a/app/views/SetUsernameView.tsx +++ b/app/views/SetUsernameView.tsx @@ -1,25 +1,26 @@ -import React from 'react'; -import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; -import { Dispatch } from 'redux'; -import { ScrollView, StyleSheet, Text } from 'react-native'; -import { connect } from 'react-redux'; -import Orientation from 'react-native-orientation-locker'; import { RouteProp } from '@react-navigation/native'; +import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; +import React from 'react'; +import { ScrollView, StyleSheet, Text } from 'react-native'; +import Orientation from 'react-native-orientation-locker'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; -import { loginRequest as loginRequestAction } from '../actions/login'; -import TextInput from '../containers/TextInput'; +import { loginRequest } from '../actions/login'; +import { themes } from '../constants/colors'; import Button from '../containers/Button'; -import KeyboardView from '../presentation/KeyboardView'; -import scrollPersistTaps from '../utils/scrollPersistTaps'; +import SafeAreaView from '../containers/SafeAreaView'; +import StatusBar from '../containers/StatusBar'; +import TextInput from '../containers/TextInput'; +import { IApplicationState } from '../definitions'; import I18n from '../i18n'; import RocketChat from '../lib/rocketchat'; -import StatusBar from '../containers/StatusBar'; -import { withTheme } from '../theme'; -import { themes } from '../constants/colors'; -import { isTablet } from '../utils/deviceInfo'; +import KeyboardView from '../presentation/KeyboardView'; import { getUserSelector } from '../selectors/login'; +import { withTheme } from '../theme'; +import { isTablet } from '../utils/deviceInfo'; import { showErrorAlert } from '../utils/info'; -import SafeAreaView from '../containers/SafeAreaView'; +import scrollPersistTaps from '../utils/scrollPersistTaps'; import sharedStyles from './Styles'; const styles = StyleSheet.create({ @@ -39,9 +40,9 @@ interface ISetUsernameViewProps { route: RouteProp<{ SetUsernameView: { title: string } }, 'SetUsernameView'>; server: string; userId: string; - loginRequest: ({ resume }: { resume: string }) => void; token: string; theme: string; + dispatch: Dispatch; } class SetUsernameView extends React.Component { @@ -86,7 +87,7 @@ class SetUsernameView extends React.Component { const { username } = this.state; - const { loginRequest, token } = this.props; + const { dispatch, token } = this.props; if (!username.trim()) { return; @@ -95,7 +96,7 @@ class SetUsernameView extends React.Component ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, token: getUserSelector(state).token }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loginRequest: (params: { resume: string }) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SetUsernameView)); +export default connect(mapStateToProps)(withTheme(SetUsernameView)); diff --git a/app/views/ShareView/index.tsx b/app/views/ShareView/index.tsx index af8b75b27..15b8053c3 100644 --- a/app/views/ShareView/index.tsx +++ b/app/views/ShareView/index.tsx @@ -29,6 +29,7 @@ import Header from './Header'; import styles from './styles'; import { IAttachment } from './interfaces'; import { ISubscription } from '../../definitions/ISubscription'; +import { IUser } from '../../definitions'; interface IShareViewState { selected: IAttachment; @@ -230,6 +231,7 @@ class ShareView extends Component { }, thread?.id, server, + // @ts-ignore { id: user.id, token: user.token } ); } @@ -239,7 +241,7 @@ class ShareView extends Component { // Send text message } else if (text.length) { - await RocketChat.sendMessage(room.rid, text, thread?.id, { id: user.id, token: user.token }); + await RocketChat.sendMessage(room.rid, text, thread?.id, { id: user.id, token: user.token } as IUser); } } catch { // Do nothing diff --git a/app/views/StatusView.tsx b/app/views/StatusView.tsx index 23fc619df..a8822df00 100644 --- a/app/views/StatusView.tsx +++ b/app/views/StatusView.tsx @@ -1,24 +1,24 @@ import React from 'react'; -import { StackNavigationProp } from '@react-navigation/stack'; import { FlatList, StyleSheet } from 'react-native'; -import { Dispatch } from 'redux'; import { connect } from 'react-redux'; -import I18n from '../i18n'; +import { UserStatus } from '../definitions/UserStatus'; +import { setUser } from '../actions/login'; +import * as HeaderButton from '../containers/HeaderButton'; import * as List from '../containers/List'; +import Loading from '../containers/Loading'; +import SafeAreaView from '../containers/SafeAreaView'; import Status from '../containers/Status/Status'; import TextInput from '../containers/TextInput'; +import { LISTENER } from '../containers/Toast'; +import { IApplicationState, IBaseScreen } from '../definitions'; +import I18n from '../i18n'; +import RocketChat from '../lib/rocketchat'; +import { getUserSelector } from '../selectors/login'; +import { withTheme } from '../theme'; import EventEmitter from '../utils/events'; import { showErrorAlert } from '../utils/info'; -import Loading from '../containers/Loading'; -import RocketChat from '../lib/rocketchat'; import log, { events, logEvent } from '../utils/log'; -import { LISTENER } from '../containers/Toast'; -import { withTheme } from '../theme'; -import { getUserSelector } from '../selectors/login'; -import * as HeaderButton from '../containers/HeaderButton'; -import { setUser as setUserAction } from '../actions/login'; -import SafeAreaView from '../containers/SafeAreaView'; const STATUS = [ { @@ -65,12 +65,9 @@ interface IStatusViewState { loading: boolean; } -interface IStatusViewProps { - navigation: StackNavigationProp; +interface IStatusViewProps extends IBaseScreen { user: IUser; - theme: string; isMasterDetail: boolean; - setUser: (user: IUser) => void; Accounts_AllowInvisibleStatusOption: boolean; } @@ -111,7 +108,7 @@ class StatusView extends React.Component { }; setCustomStatus = async (statusText: string) => { - const { user, setUser } = this.props; + const { user, dispatch } = this.props; this.setState({ loading: true }); @@ -119,7 +116,7 @@ class StatusView extends React.Component { const result = await RocketChat.setUserStatus(user.status, statusText); if (result.success) { logEvent(events.STATUS_CUSTOM); - setUser({ statusText }); + dispatch(setUser({ statusText })); EventEmitter.emit(LISTENER, { message: I18n.t('Status_saved_successfully') }); } else { logEvent(events.STATUS_CUSTOM_F); @@ -156,7 +153,7 @@ class StatusView extends React.Component { renderItem = ({ item }: { item: { id: string; name: string } }) => { const { statusText } = this.state; - const { user, setUser } = this.props; + const { user, dispatch } = this.props; const { id, name } = item; return ( { try { const result = await RocketChat.setUserStatus(item.id, statusText); if (result.success) { - setUser({ status: item.id }); + dispatch(setUser({ status: item.id as UserStatus })); } } catch (e: any) { showErrorAlert(I18n.t(e.data.errorType)); @@ -205,14 +202,10 @@ class StatusView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ user: getUserSelector(state), isMasterDetail: state.app.isMasterDetail, - Accounts_AllowInvisibleStatusOption: state.settings.Accounts_AllowInvisibleStatusOption ?? true + Accounts_AllowInvisibleStatusOption: (state.settings.Accounts_AllowInvisibleStatusOption as boolean) ?? true }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ - setUser: (user: IUser) => dispatch(setUserAction(user)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(StatusView)); +export default connect(mapStateToProps)(withTheme(StatusView)); diff --git a/app/views/TeamChannelsView.tsx b/app/views/TeamChannelsView.tsx index d82ae7053..a76c18ba8 100644 --- a/app/views/TeamChannelsView.tsx +++ b/app/views/TeamChannelsView.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { Alert, FlatList, Keyboard } from 'react-native'; import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context'; import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; import { deleteRoom } from '../actions/room'; import { themes } from '../constants/colors'; @@ -17,6 +18,7 @@ import SafeAreaView from '../containers/SafeAreaView'; import SearchHeader from '../containers/SearchHeader'; import StatusBar from '../containers/StatusBar'; import { IApplicationState, IBaseScreen } from '../definitions'; +import { ERoomType } from '../definitions/ERoomType'; import { withDimensions } from '../dimensions'; import I18n from '../i18n'; import database from '../lib/database'; @@ -48,7 +50,7 @@ const keyExtractor = (item: IItem) => item._id; // This interface comes from request RocketChat.getTeamListRoom interface IItem { - _id: string; + _id: ERoomType; fname: string; customFields: object; broadcast: boolean; @@ -97,6 +99,7 @@ interface ITeamChannelsViewProps extends IProps { showActionSheet: (options: any) => void; showAvatar: boolean; displayMode: string; + dispatch: Dispatch; } class TeamChannelsView extends React.Component { private teamId: string; @@ -438,7 +441,8 @@ class TeamChannelsView extends React.Component dispatch(deleteRoom(item._id, item.t)) + // VERIFY ON PR + onPress: () => dispatch(deleteRoom(item._id, item)) } ], { cancelable: false } diff --git a/patches/@types+ejson+2.1.3.patch b/patches/@types+ejson+2.1.3.patch new file mode 100644 index 000000000..cf404b43f --- /dev/null +++ b/patches/@types+ejson+2.1.3.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@types/ejson/index.d.ts b/node_modules/@types/ejson/index.d.ts +index 3a35636..278ef98 100755 +--- a/node_modules/@types/ejson/index.d.ts ++++ b/node_modules/@types/ejson/index.d.ts +@@ -17,7 +17,7 @@ export function parse(str: string): any; + export function stringify(obj: any, options?: StringifyOptions): string; + + export function toJSONValue(obj: any): string; +-export function fromJSONValue(obj: string): any; ++export function fromJSONValue(obj: Object): any; + export function isBinary(value: any): boolean; + export function newBinary(len: number): Uint8Array; + export function equals(a: any, b: any, options?: CloneOptions): boolean; diff --git a/storybook/stories/Message.js b/storybook/stories/Message.js index 30db507b0..600f77016 100644 --- a/storybook/stories/Message.js +++ b/storybook/stories/Message.js @@ -797,6 +797,13 @@ stories.add('System messages', () => ( + + + + + + + )); diff --git a/storybook/stories/__snapshots__/Message.storyshot b/storybook/stories/__snapshots__/Message.storyshot index 0aefed58b..ac9fa7a75 100644 --- a/storybook/stories/__snapshots__/Message.storyshot +++ b/storybook/stories/__snapshots__/Message.storyshot @@ -54,7 +54,7 @@ exports[`Storyshots Message Show a button as attachment 1`] = `"{\\"type\\":\\"R exports[`Storyshots Message Static avatar 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://pbs.twimg.com/profile_images/1016397063649660929/14EIApTi_400x400.jpg\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":12,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},null,{\\"color\\":\\"#2f343d\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Message\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"Message\\"]}]}]}]}]}]}]}]}]}]}"`; -exports[`Storyshots Message System messages 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Message removed\\"},\\"children\\":[\\"Message removed\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"has joined the channel\\"},\\"children\\":[\\"has joined the channel\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Message pinned\\"},\\"children\\":[\\"Message pinned\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"has left the channel\\"},\\"children\\":[\\"has left the channel\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat removed by diego.mello\\"},\\"children\\":[\\"User rocket.cat removed by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat added by diego.mello\\"},\\"children\\":[\\"User rocket.cat added by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat muted by diego.mello\\"},\\"children\\":[\\"User rocket.cat muted by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat unmuted by diego.mello\\"},\\"children\\":[\\"User rocket.cat unmuted by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"rocket.cat was set admin by diego.mello\\"},\\"children\\":[\\"rocket.cat was set admin by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"rocket.cat is no longer admin by diego.mello\\"},\\"children\\":[\\"rocket.cat is no longer admin by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room name changed to: New name by diego.mello\\"},\\"children\\":[\\"Room name changed to: New name by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room description changed to: new description by diego.mello\\"},\\"children\\":[\\"Room description changed to: new description by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room announcement changed to: new announcement by diego.mello\\"},\\"children\\":[\\"Room announcement changed to: new announcement by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room topic changed to: new topic by diego.mello\\"},\\"children\\":[\\"Room topic changed to: new topic by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room type changed to: public by diego.mello\\"},\\"children\\":[\\"Room type changed to: public by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"This room's encryption has been disabled by diego.mello\\"},\\"children\\":[\\"This room's encryption has been disabled by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"This room's encryption has been enabled by diego.mello\\"},\\"children\\":[\\"This room's encryption has been enabled by diego.mello\\"]}]}]}]}]}]}]}]}"`; +exports[`Storyshots Message System messages 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Message removed\\"},\\"children\\":[\\"Message removed\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"has joined the channel\\"},\\"children\\":[\\"has joined the channel\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Message pinned\\"},\\"children\\":[\\"Message pinned\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"has left the channel\\"},\\"children\\":[\\"has left the channel\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat removed by diego.mello\\"},\\"children\\":[\\"User rocket.cat removed by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat added by diego.mello\\"},\\"children\\":[\\"User rocket.cat added by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat muted by diego.mello\\"},\\"children\\":[\\"User rocket.cat muted by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"User rocket.cat unmuted by diego.mello\\"},\\"children\\":[\\"User rocket.cat unmuted by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"rocket.cat was set admin by diego.mello\\"},\\"children\\":[\\"rocket.cat was set admin by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"rocket.cat is no longer admin by diego.mello\\"},\\"children\\":[\\"rocket.cat is no longer admin by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room name changed to: New name by diego.mello\\"},\\"children\\":[\\"Room name changed to: New name by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room description changed to: new description by diego.mello\\"},\\"children\\":[\\"Room description changed to: new description by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room announcement changed to: new announcement by diego.mello\\"},\\"children\\":[\\"Room announcement changed to: new announcement by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room topic changed to: new topic by diego.mello\\"},\\"children\\":[\\"Room topic changed to: new topic by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"Room type changed to: public by diego.mello\\"},\\"children\\":[\\"Room type changed to: public by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"This room's encryption has been disabled by diego.mello\\"},\\"children\\":[\\"This room's encryption has been disabled by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"This room's encryption has been enabled by diego.mello\\"},\\"children\\":[\\"This room's encryption has been enabled by diego.mello\\"]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"removed @diego.mello from this Team\\"},\\"children\\":[\\"removed @diego.mello from this Team\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"added @diego.mello to this Team\\"},\\"children\\":[\\"added @diego.mello to this Team\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"added #channel-name to this Team\\"},\\"children\\":[\\"added #channel-name to this Team\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"converted #channel-name to a Team\\"},\\"children\\":[\\"converted #channel-name to a Team\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"converted #channel-name to a Channel\\"},\\"children\\":[\\"converted #channel-name to a Channel\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"deleted #channel-name\\"},\\"children\\":[\\"deleted #channel-name\\"]}]}]}]}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2},{\\"marginLeft\\":16}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":20,\\"height\\":20,\\"borderRadius\\":2}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=20\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"disabled\\":false},\\"children\\":[\\"diego.mello\\"]},\\" \\",{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontStyle\\":\\"italic\\",\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}],\\"accessibilityLabel\\":\\"removed #channel-name from this Team\\"},\\"children\\":[\\"removed #channel-name from this Team\\"]}]}]}]}]}]}]}]}]}"`; exports[`Storyshots Message Temp 1`] = `"{\\"type\\":\\"RCTScrollView\\",\\"props\\":{\\"style\\":{\\"backgroundColor\\":\\"#ffffff\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"backgroundColor\\":null,\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"paddingVertical\\":4,\\"width\\":\\"100%\\",\\"paddingHorizontal\\":14,\\"flexDirection\\":\\"column\\"},null]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flexDirection\\":\\"row\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4},{\\"marginTop\\":4}],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":36,\\"height\\":36,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=36\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"flex\\":1,\\"marginLeft\\":46},{\\"marginLeft\\":10}]},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"flex\\":1,\\"flexDirection\\":\\"row\\",\\"justifyContent\\":\\"space-between\\",\\"alignItems\\":\\"center\\"}},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"accessible\\":true,\\"focusable\\":true,\\"style\\":{\\"flexShrink\\":1,\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"center\\",\\"opacity\\":1}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"lineHeight\\":22,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"500\\"},{\\"color\\":\\"#0d0e12\\"}],\\"numberOfLines\\":1},\\"children\\":[\\"diego.mello\\"]}]},{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":12,\\"marginLeft\\":8,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},{\\"color\\":\\"#9ca2a8\\"}]},\\"children\\":[\\"10:00 AM\\"]}]},{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":{\\"opacity\\":0.3}},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},null,{\\"color\\":\\"#2f343d\\"}]},\\"children\\":[{\\"type\\":\\"Text\\",\\"props\\":{\\"accessibilityLabel\\":\\"Temp message\\",\\"style\\":[{\\"fontSize\\":16,\\"textAlign\\":\\"left\\",\\"backgroundColor\\":\\"transparent\\",\\"fontFamily\\":\\"System\\",\\"fontWeight\\":\\"400\\"},[{},{\\"marginTop\\":0,\\"marginBottom\\":0,\\"flexWrap\\":\\"wrap\\",\\"flexDirection\\":\\"row\\",\\"alignItems\\":\\"flex-start\\",\\"justifyContent\\":\\"flex-start\\"}]]},\\"children\\":[\\"Temp message\\"]}]}]}]}]}]}]}]}]}]}"`;