diff --git a/android/app/build.gradle b/android/app/build.gradle index fe11088a0..54ef907cf 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -147,7 +147,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode VERSIONCODE as Integer - versionName "4.33.0" + versionName "4.33.1" vectorDrawables.useSupportLibrary = true if (!isFoss) { manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String] diff --git a/app/containers/MessageActions/Header.tsx b/app/containers/MessageActions/Header.tsx index 26d35d4cc..f9c809e5e 100644 --- a/app/containers/MessageActions/Header.tsx +++ b/app/containers/MessageActions/Header.tsx @@ -14,12 +14,12 @@ import { IEmoji, TAnyMessageModel } from '../../definitions'; import Touch from '../Touch'; export interface IHeader { - handleReaction: (emoji: IEmoji, message: TAnyMessageModel) => void; + handleReaction: (emoji: IEmoji | null, message: TAnyMessageModel) => void; message: TAnyMessageModel; isMasterDetail: boolean; } -type TOnReaction = ({ emoji }: { emoji: IEmoji }) => void; +type TOnReaction = ({ emoji }: { emoji?: IEmoji }) => void; interface THeaderItem { item: IEmoji; @@ -94,8 +94,10 @@ const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader) const quantity = Math.trunc(size / (ITEM_SIZE + ITEM_MARGIN * 2) - 1); const onReaction: TOnReaction = ({ emoji }) => { - handleReaction(emoji, message); - addFrequentlyUsed(emoji); + handleReaction(emoji || null, message); + if (emoji) { + addFrequentlyUsed(emoji); + } }; const renderItem = ({ item }: { item: IEmoji }) => ; diff --git a/app/containers/MessageActions/index.tsx b/app/containers/MessageActions/index.tsx index 925d14613..38d77c14e 100644 --- a/app/containers/MessageActions/index.tsx +++ b/app/containers/MessageActions/index.tsx @@ -285,10 +285,10 @@ const MessageActions = React.memo( } }; - const handleReaction: IHeader['handleReaction'] = (shortname, message) => { + const handleReaction: IHeader['handleReaction'] = (emoji, message) => { logEvent(events.ROOM_MSG_ACTION_REACTION); - if (shortname) { - onReactionPress(shortname, message.id); + if (emoji) { + onReactionPress(emoji, message.id); } else { setTimeout(() => reactionInit(message), ACTION_SHEET_ANIMATION_DURATION); } diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index e62df8189..3fe3ea01b 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -565,6 +565,8 @@ "Unsupported_system_message": "Unsupported system message", "Updating": "Updating...", "Uploading": "Uploading", + "FileUpload_Error": "File Upload Error", + "Upload_in_progress": "Upload in progress", "Upload_file_question_mark": "Upload file?", "User": "User", "Users": "Users", diff --git a/app/i18n/locales/pt-BR.json b/app/i18n/locales/pt-BR.json index 3bb4de322..014d785a1 100644 --- a/app/i18n/locales/pt-BR.json +++ b/app/i18n/locales/pt-BR.json @@ -513,6 +513,8 @@ "Unsupported_system_message": "Mensagem de sistema não suportada", "Updating": "Atualizando...", "Uploading": "Subindo arquivo", + "FileUpload_Error": "Erro de upload de arquivo", + "Upload_in_progress": "Carregamento em andamento", "Upload_file_question_mark": "Enviar arquivo?", "User": "Usuário", "Users": "Usuários", diff --git a/app/lib/methods/sendFileMessage.ts b/app/lib/methods/sendFileMessage.ts index c8facc473..dbbe6317e 100644 --- a/app/lib/methods/sendFileMessage.ts +++ b/app/lib/methods/sendFileMessage.ts @@ -1,27 +1,35 @@ 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 { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; +import { Alert } from 'react-native'; -import FileUpload from './helpers/fileUpload'; -import database from '../database'; -import log from './helpers/log'; import { IUpload, IUser, TUploadModel } from '../../definitions'; +import i18n from '../../i18n'; +import database from '../database'; +import FileUpload from './helpers/fileUpload'; import { IFileUpload } from './helpers/fileUpload/interfaces'; +import log from './helpers/log'; const uploadQueue: { [index: string]: StatefulPromise } = {}; -export function isUploadActive(path: string): boolean { - return !!uploadQueue[path]; +const getUploadPath = (path: string, rid: string) => `${path}-${rid}`; + +export function isUploadActive(path: string, rid: string): boolean { + return !!uploadQueue[getUploadPath(path, rid)]; } -export async function cancelUpload(item: TUploadModel): Promise { - if (!isEmpty(uploadQueue[item.path])) { +export async function cancelUpload(item: TUploadModel, rid: string): Promise { + const uploadPath = getUploadPath(item.path, rid); + if (!isEmpty(uploadQueue[uploadPath])) { try { - await uploadQueue[item.path].cancel(); + await uploadQueue[uploadPath].cancel(); } catch { // Do nothing } + delete uploadQueue[uploadPath]; + } + if (item.id) { try { const db = database.active; await db.write(async () => { @@ -30,7 +38,6 @@ export async function cancelUpload(item: TUploadModel): Promise { } catch (e) { log(e); } - delete uploadQueue[item.path]; } } @@ -51,14 +58,18 @@ export function sendFileMessage( const db = database.active; const uploadsCollection = db.get('uploads'); + const uploadPath = getUploadPath(fileInfo.path, rid); let uploadRecord: TUploadModel; try { - uploadRecord = await uploadsCollection.find(fileInfo.path); + uploadRecord = await uploadsCollection.find(uploadPath); + if (uploadRecord.id) { + return Alert.alert(i18n.t('FileUpload_Error'), i18n.t('Upload_in_progress')); + } } catch (error) { try { await db.write(async () => { uploadRecord = await uploadsCollection.create(u => { - u._raw = sanitizedRaw({ id: fileInfo.path }, uploadsCollection.schema); + u._raw = sanitizedRaw({ id: uploadPath }, uploadsCollection.schema); Object.assign(u, fileInfo); if (u.subscription) { u.subscription.id = rid; @@ -99,9 +110,9 @@ export function sendFileMessage( 'X-User-Id': id }; - uploadQueue[fileInfo.path] = FileUpload.fetch('POST', uploadUrl, headers, formData); + uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData); - uploadQueue[fileInfo.path].uploadProgress(async (loaded: number, total: number) => { + uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => { try { await db.write(async () => { await uploadRecord.update(u => { @@ -113,7 +124,7 @@ export function sendFileMessage( } }); - uploadQueue[fileInfo.path].then(async response => { + uploadQueue[uploadPath].then(async response => { if (response.respInfo.status >= 200 && response.respInfo.status < 400) { // If response is all good... try { @@ -142,7 +153,7 @@ export function sendFileMessage( } }); - uploadQueue[fileInfo.path].catch(async error => { + uploadQueue[uploadPath].catch(async error => { try { await db.write(async () => { await uploadRecord.update(u => { diff --git a/app/views/RoomView/UploadProgress.tsx b/app/views/RoomView/UploadProgress.tsx index a6791c3e2..2509870c9 100644 --- a/app/views/RoomView/UploadProgress.tsx +++ b/app/views/RoomView/UploadProgress.tsx @@ -112,9 +112,10 @@ class UploadProgress extends Component { this.ranInitialUploadCheck = true; + const { rid } = this.props; const { uploads } = this.state; uploads.forEach(async u => { - if (!isUploadActive(u.path)) { + if (!isUploadActive(u.path, rid)) { try { const db = database.active; await db.write(async () => { @@ -141,8 +142,9 @@ class UploadProgress extends Component { + const { rid } = this.props; try { - await cancelUpload(item); + await cancelUpload(item, rid); } catch (e) { log(e); } @@ -210,7 +212,7 @@ class UploadProgress extends Component { showReactionPicker = () => { const { showActionSheet } = this.props; const { selectedMessage } = this.state; - showActionSheet({ - children: ( - - ), - snaps: [400], - enableContentPanningGesture: false - }); + setTimeout(() => { + showActionSheet({ + children: ( + + ), + snaps: [400], + enableContentPanningGesture: false + }); + }, 100); }; onReactionInit = (message: TAnyMessageModel) => { diff --git a/app/views/ThreadMessagesView/index.tsx b/app/views/ThreadMessagesView/index.tsx index 2aee455b8..2769b3302 100644 --- a/app/views/ThreadMessagesView/index.tsx +++ b/app/views/ThreadMessagesView/index.tsx @@ -142,7 +142,7 @@ class ThreadMessagesView extends React.Component ( - + ) }; @@ -242,7 +242,7 @@ class ThreadMessagesView extends React.Component ({ messages: [...messages, ...update] })); return; } diff --git a/e2e/tests/assorted/09-joinfromdirectory.spec.ts b/e2e/tests/assorted/09-joinfromdirectory.spec.ts index ab181b9ed..c9e19efb2 100644 --- a/e2e/tests/assorted/09-joinfromdirectory.spec.ts +++ b/e2e/tests/assorted/09-joinfromdirectory.spec.ts @@ -1,5 +1,6 @@ import data from '../../data'; import { navigateToLogin, login, tapBack, sleep } from '../../helpers/app'; +import { sendMessage } from '../../helpers/data_setup'; const testuser = data.users.regular; @@ -26,6 +27,13 @@ describe('Join room from directory', () => { }); describe('Usage', () => { + const threadMessage = `thread-${data.random}`; + before(async () => { + const result = await sendMessage(data.users.alternate, data.channels.detoxpublic.name, threadMessage); + const threadId = result.message._id; + await sendMessage(data.users.alternate, result.message.rid, data.random, threadId); + }); + it('should tap directory', async () => { await element(by.id('rooms-list-view-directory')).tap(); await waitFor(element(by.id('directory-view'))) @@ -37,6 +45,20 @@ describe('Join room from directory', () => { await navigateToRoom(data.channels.detoxpublic.name); }); + it('should navigate to thread messages view and load messages', async () => { + await waitFor(element(by.id('room-view-header-threads'))) + .toBeVisible() + .withTimeout(2000); + await element(by.id('room-view-header-threads')).tap(); + await waitFor(element(by.id(`thread-messages-view-${threadMessage}`))) + .toBeVisible() + .withTimeout(2000); + await tapBack(); + await waitFor(element(by.id('room-view-header-threads'))) + .toBeVisible() + .withTimeout(2000); + }); + it('should search user and navigate', async () => { await tapBack(); await element(by.id('rooms-list-view-directory')).tap(); diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 1ea9c760d..0ec07074b 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -1767,7 +1767,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.33.0; + MARKETING_VERSION = 4.33.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -1806,7 +1806,7 @@ INFOPLIST_FILE = NotificationService/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.33.0; + MARKETING_VERSION = 4.33.1; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = chat.rocket.reactnative.NotificationService; diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index 53af2cee5..f2923f6f0 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 4.33.0 + 4.33.1 CFBundleSignature ???? CFBundleURLTypes diff --git a/ios/ShareRocketChatRN/Info.plist b/ios/ShareRocketChatRN/Info.plist index 27a4c4b3a..95f67d724 100644 --- a/ios/ShareRocketChatRN/Info.plist +++ b/ios/ShareRocketChatRN/Info.plist @@ -26,7 +26,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 4.33.0 + 4.33.1 CFBundleVersion 1 KeychainGroup diff --git a/package.json b/package.json index 04b433fc5..7df1d31bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rocket-chat-reactnative", - "version": "4.33.0", + "version": "4.33.1", "private": true, "scripts": { "start": "react-native start", diff --git a/yarn.lock b/yarn.lock index bc9ac843c..4f3e5d74e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17379,7 +17379,7 @@ react-native-text-size@4.0.0-rc.1: react-native-ui-lib@RocketChat/react-native-ui-lib: version "4.2.0" - resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/d20c1bcd09b694fc5133fc2232fd510f5f4ba581" + resolved "https://codeload.github.com/RocketChat/react-native-ui-lib/tar.gz/fd5869e493b5b9cf888cec4a252c9ef292364b02" dependencies: babel-plugin-transform-inline-environment-variables "^0.0.2" color "^3.1.0"