From f24a8d560e009d172ed6eb436e9a80a050795c99 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 5 Jul 2017 18:15:40 -0300 Subject: [PATCH 1/7] Run avatar migration after server startup --- server/startup/migrations/v097.js | 116 +++++++++++++++--------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/server/startup/migrations/v097.js b/server/startup/migrations/v097.js index 1ec5ae82c8e..a283984d5bb 100644 --- a/server/startup/migrations/v097.js +++ b/server/startup/migrations/v097.js @@ -117,75 +117,77 @@ RocketChat.Migrations.add({ const avatarsPath = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStorePath'}).value; const avatarStoreType = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStoreType'}).value; - Meteor.setTimeout(function() { - const avatarsFileStore = FileUpload.getStore('Avatars'); + Meteor.startup(function() { + Meteor.setTimeout(function() { + const avatarsFileStore = FileUpload.getStore('Avatars'); - const oldAvatarGridFS = new RocketChatFile.GridFS({ - name: 'avatars' - }); + const oldAvatarGridFS = new RocketChatFile.GridFS({ + name: 'avatars' + }); - const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch(); + const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch(); - const usersTotal = users.length; + const usersTotal = users.length; - log('Total users to migrate avatars ->', usersTotal); + log('Total users to migrate avatars ->', usersTotal); - let current = 0; + let current = 0; - batch(users, 300, (user) => { - const id = `${ user.username }.jpg`; + batch(users, 300, (user) => { + const id = `${ user.username }.jpg`; - const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id); + const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id); - log('Migrating', ++current, 'of', usersTotal); + log('Migrating', ++current, 'of', usersTotal); - if (gridFSAvatar) { - const details = { - userId: user._id, - type: gridFSAvatar.contentType, - size: gridFSAvatar.length - }; + if (gridFSAvatar) { + const details = { + userId: user._id, + type: gridFSAvatar.contentType, + size: gridFSAvatar.length + }; - return insertAvatar({ - details, - avatarsFileStore, - stream: gridFSAvatar.readStream, - callback() { - oldAvatarGridFS.deleteFile(id); - } - }); - } - if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) { - try { - const filePath = path.join(avatarsPath, id); - const stat = fs.statSync(filePath); - if (stat && stat.isFile()) { - const details = { - userId: user._id, - type: 'image/jpeg', - size: stat.size - }; - return insertAvatar({ - details, - avatarsFileStore, - stream: fs.createReadStream(filePath), - callback() { - fs.unlinkSync(filePath); - } - }); + return insertAvatar({ + details, + avatarsFileStore, + stream: gridFSAvatar.readStream, + callback() { + oldAvatarGridFS.deleteFile(id); + } + }); + } + if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) { + try { + const filePath = path.join(avatarsPath, id); + const stat = fs.statSync(filePath); + if (stat && stat.isFile()) { + const details = { + userId: user._id, + type: 'image/jpeg', + size: stat.size + }; + return insertAvatar({ + details, + avatarsFileStore, + stream: fs.createReadStream(filePath), + callback() { + fs.unlinkSync(filePath); + } + }); + } + } catch (e) { + logError('Error migrating old avatar', e); + return Promise.resolve(); } - } catch (e) { - logError('Error migrating old avatar', e); - return Promise.resolve(); } - } - }).then(() => { - const avatarsFiles = new Mongo.Collection('avatars.files'); - const avatarsChunks = new Mongo.Collection('avatars.chunks'); - avatarsFiles.rawCollection().drop(); - avatarsChunks.rawCollection().drop(); - }); - }, 1000); + }).then(() => { + const avatarsFiles = new Mongo.Collection('avatars.files'); + const avatarsChunks = new Mongo.Collection('avatars.chunks'); + avatarsFiles.rawCollection().drop(); + avatarsChunks.rawCollection().drop(); + }); + }, 1000); + }); RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStoreType'}); RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStorePath'}); } From a3ce28a03d4c3538c857f5782920579167fa9130 Mon Sep 17 00:00:00 2001 From: Pierre Ozoux Date: Thu, 6 Jul 2017 00:27:03 +0100 Subject: [PATCH 2/7] Add helm chart kubernetes deployment (#6340) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1ef374f3383..a1de201ca5a 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ * [Ubuntu 16.04](#ubuntu-1604) * [Cloudron.io](#cloudronio) * [Heroku](#heroku) + * [Helm Kubernetes](#helm-kubernetes) * [Scalingo](#scalingo) * [Sloppy.io](#sloppyio) * [Docker](#docker) @@ -137,6 +138,9 @@ Host your own Rocket.Chat server for **FREE** with [One-Click Deploy](https://he [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy?template=https://github.com/RocketChat/Rocket.Chat/tree/master) +## Helm Kubernetes +Deploy on Kubernetes using the official [helm chart](https://github.com/kubernetes/charts/pull/752). + ## Scalingo Deploy your own Rocket.Chat server instantly on [Scalingo](https://scalingo.com) From 892b469878cd08ec6915f5ff76c5792476c634c0 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Wed, 5 Jul 2017 22:01:55 -0300 Subject: [PATCH 3/7] Move migration 97 to 99 --- packages/rocketchat-file/file.server.js | 1 + server/startup/migrations/v097.js | 190 +-------------------- server/startup/migrations/v099.js | 217 ++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 189 deletions(-) create mode 100644 server/startup/migrations/v099.js diff --git a/packages/rocketchat-file/file.server.js b/packages/rocketchat-file/file.server.js index 500513a58e5..1fb0487184e 100644 --- a/packages/rocketchat-file/file.server.js +++ b/packages/rocketchat-file/file.server.js @@ -105,6 +105,7 @@ RocketChatFile.GridFS = class { this.store = new Grid(db, mongo); this.findOneSync = Meteor.wrapAsync(this.store.collection(this.name).findOne.bind(this.store.collection(this.name))); this.removeSync = Meteor.wrapAsync(this.store.remove.bind(this.store)); + this.countSync = Meteor.wrapAsync(this.store._col.count.bind(this.store._col)); this.getFileSync = Meteor.wrapAsync(this.getFile.bind(this)); } diff --git a/server/startup/migrations/v097.js b/server/startup/migrations/v097.js index a283984d5bb..bd6e421ac7a 100644 --- a/server/startup/migrations/v097.js +++ b/server/startup/migrations/v097.js @@ -1,194 +1,6 @@ -import fs from 'fs'; -import path from 'path'; - -function log(...args) { - console.log('[AVATAR]', ...args); -} - -function logError(...args) { - console.error('[AVATAR]', ...args); -} - -function insertAvatar({ details, avatarsFileStore, stream, callback = () => {} }) { - return new Promise((resolve) => { - Meteor.defer(() => { - Meteor.runAsUser('rocket.cat', () => { - avatarsFileStore.insert(details, stream, (err) => { - if (err) { - logError({err}); - resolve(); - } else { - Meteor.setTimeout(() => { - callback(); - }, 200); - } - resolve(); - }); - }); - }); - }); -} - -function batch(arr, limit, fn) { - if (!arr.length) { - return Promise.resolve(); - } - return Promise.all(arr.splice(0, limit).map((item) => { - return fn(item); - })).then(() => { return batch(arr, limit, fn); }); -} - RocketChat.Migrations.add({ version: 97, up() { - log('Migrating avatars. This might take a while.'); - - const query = { - $or: [{ - 's3.path': { - $exists: true - } - }, { - 'googleCloudStorage.path': { - $exists: true - } - }] - }; - - RocketChat.models.Uploads.find(query).forEach((record) => { - if (record.s3) { - RocketChat.models.Uploads.model.direct.update({_id: record._id}, { - $set: { - 'store': 'AmazonS3:Uploads', - AmazonS3: { - path: record.s3.path + record._id - } - }, - $unset: { - s3: 1 - } - }, {multi: true}); - } else { - RocketChat.models.Uploads.model.direct.update({_id: record._id}, { - $set: { - store: 'GoogleCloudStorage:Uploads', - GoogleStorage: { - path: record.googleCloudStorage.path + record._id - } - }, - $unset: { - googleCloudStorage: 1 - } - }, {multi: true}); - } - }); - - RocketChat.models.Uploads.model.direct.update({ - store: 'fileSystem' - }, { - $set: { - store: 'FileSystem:Uploads' - } - }, { - multi: true - }); - RocketChat.models.Uploads.model.direct.update({ - store: 'rocketchat_uploads' - }, { - $set: { - store: 'GridFS:Uploads' - } - }, { - multi: true - }); - - const avatarOrigins = [ - 'upload', - 'gravatar', - 'facebook', - 'twitter', - 'github', - 'google', - 'url', - 'gitlab', - 'linkedin' - ]; - - const avatarsPath = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStorePath'}).value; - const avatarStoreType = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStoreType'}).value; - - Meteor.startup(function() { - Meteor.setTimeout(function() { - const avatarsFileStore = FileUpload.getStore('Avatars'); - - const oldAvatarGridFS = new RocketChatFile.GridFS({ - name: 'avatars' - }); - - const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch(); - - const usersTotal = users.length; - - log('Total users to migrate avatars ->', usersTotal); - - let current = 0; - - batch(users, 300, (user) => { - const id = `${ user.username }.jpg`; - - const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id); - - log('Migrating', ++current, 'of', usersTotal); - - if (gridFSAvatar) { - const details = { - userId: user._id, - type: gridFSAvatar.contentType, - size: gridFSAvatar.length - }; - - return insertAvatar({ - details, - avatarsFileStore, - stream: gridFSAvatar.readStream, - callback() { - oldAvatarGridFS.deleteFile(id); - } - }); - } - if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) { - try { - const filePath = path.join(avatarsPath, id); - const stat = fs.statSync(filePath); - if (stat && stat.isFile()) { - const details = { - userId: user._id, - type: 'image/jpeg', - size: stat.size - }; - return insertAvatar({ - details, - avatarsFileStore, - stream: fs.createReadStream(filePath), - callback() { - fs.unlinkSync(filePath); - } - }); - } - } catch (e) { - logError('Error migrating old avatar', e); - return Promise.resolve(); - } - } - }).then(() => { - const avatarsFiles = new Mongo.Collection('avatars.files'); - const avatarsChunks = new Mongo.Collection('avatars.chunks'); - avatarsFiles.rawCollection().drop(); - avatarsChunks.rawCollection().drop(); - }); - }, 1000); - }); - RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStoreType'}); - RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStorePath'}); + // Migration moved to 099.js to fix a bug } }); diff --git a/server/startup/migrations/v099.js b/server/startup/migrations/v099.js new file mode 100644 index 00000000000..43344c5e5d0 --- /dev/null +++ b/server/startup/migrations/v099.js @@ -0,0 +1,217 @@ +/* globals SystemLogger */ + +import fs from 'fs'; +import path from 'path'; + +function log(...args) { + console.log('[AVATAR]', ...args); +} + +function logError(...args) { + console.error('[AVATAR]', ...args); +} + +function insertAvatar({ details, avatarsFileStore, stream, callback = () => {} }) { + return new Promise((resolve) => { + Meteor.defer(() => { + Meteor.runAsUser('rocket.cat', () => { + avatarsFileStore.insert(details, stream, (err) => { + if (err) { + logError({err}); + resolve(); + } else { + Meteor.setTimeout(() => { + callback(); + }, 200); + } + resolve(); + }); + }); + }); + }); +} + +function batch(arr, limit, fn) { + if (!arr.length) { + return Promise.resolve(); + } + return Promise.all(arr.splice(0, limit).map((item) => { + return fn(item); + })).then(() => { return batch(arr, limit, fn); }); +} + +RocketChat.Migrations.add({ + version: 99, + up() { + log('Migrating avatars. This might take a while.'); + + const query = { + $or: [{ + 's3.path': { + $exists: true + } + }, { + 'googleCloudStorage.path': { + $exists: true + } + }] + }; + + RocketChat.models.Uploads.find(query).forEach((record) => { + if (record.s3) { + RocketChat.models.Uploads.model.direct.update({_id: record._id}, { + $set: { + 'store': 'AmazonS3:Uploads', + AmazonS3: { + path: record.s3.path + record._id + } + }, + $unset: { + s3: 1 + } + }, {multi: true}); + } else { + RocketChat.models.Uploads.model.direct.update({_id: record._id}, { + $set: { + store: 'GoogleCloudStorage:Uploads', + GoogleStorage: { + path: record.googleCloudStorage.path + record._id + } + }, + $unset: { + googleCloudStorage: 1 + } + }, {multi: true}); + } + }); + + RocketChat.models.Uploads.model.direct.update({ + store: 'fileSystem' + }, { + $set: { + store: 'FileSystem:Uploads' + } + }, { + multi: true + }); + RocketChat.models.Uploads.model.direct.update({ + store: 'rocketchat_uploads' + }, { + $set: { + store: 'GridFS:Uploads' + } + }, { + multi: true + }); + + const avatarOrigins = [ + 'upload', + 'gravatar', + 'facebook', + 'twitter', + 'github', + 'google', + 'url', + 'gitlab', + 'linkedin' + ]; + + const avatarsPathRecord = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStorePath'}); + const avatarStoreTypeRecord = RocketChat.models.Settings.findOne({_id: 'Accounts_AvatarStoreType'}); + + const avatarsPath = avatarsPathRecord ? avatarsPathRecord.value : process.env.AVATARS_PATH; + let avatarStoreType = avatarStoreTypeRecord && avatarStoreTypeRecord.value; + + const oldAvatarGridFS = new RocketChatFile.GridFS({ + name: 'avatars' + }); + + // 1. Novo usuário, não executa migration + // 2. Antigo, migra de antes da 97, vai ter storage type + // 3. Executou a 97, não vai ter storeType + // 3.1 Se ainda tem dados no gridfs, manda ver + // 3.2 Se não tem, quebra e pede a variável OU forçar a reexecutar a 97??? + + if (avatarStoreType == null) { + const count = oldAvatarGridFS.countSync(); + if (Match.test(count, Number) && count > 0) { + avatarStoreType = 'GridFS'; + } else if (Match.test(avatarsPath, String) && avatarsPath.length > 0) { + avatarStoreType = 'FileSystem'; + } else { + SystemLogger.error_box('Can\'t define the avatar\'s storage type.\nIf you have avatars missing and they was stored in your file system\nrun the process including the following environment variables: \n AVATARS_PATH=\'YOUR AVATAR\'S DIRECTORY\'\n MIGRATION_VERSION=98', 'WARNING'); + return; + } + } + + Meteor.startup(function() { + Meteor.setTimeout(function() { + const avatarsFileStore = FileUpload.getStore('Avatars'); + + const users = RocketChat.models.Users.find({avatarOrigin: {$in: avatarOrigins}}, {avatarOrigin: 1, username: 1}).fetch(); + + const usersTotal = users.length; + + log('Total users to migrate avatars ->', usersTotal); + + let current = 0; + + batch(users, 300, (user) => { + const id = `${ user.username }.jpg`; + + const gridFSAvatar = oldAvatarGridFS.getFileWithReadStream(id); + + log('Migrating', ++current, 'of', usersTotal); + + if (gridFSAvatar) { + const details = { + userId: user._id, + type: gridFSAvatar.contentType, + size: gridFSAvatar.length + }; + + return insertAvatar({ + details, + avatarsFileStore, + stream: gridFSAvatar.readStream, + callback() { + oldAvatarGridFS.deleteFile(id); + } + }); + } + if (avatarStoreType === 'FileSystem' && avatarsPath && avatarsPath.trim()) { + try { + const filePath = path.join(avatarsPath, id); + const stat = fs.statSync(filePath); + if (stat && stat.isFile()) { + const details = { + userId: user._id, + type: 'image/jpeg', + size: stat.size + }; + return insertAvatar({ + details, + avatarsFileStore, + stream: fs.createReadStream(filePath), + callback() { + fs.unlinkSync(filePath); + } + }); + } + } catch (e) { + logError('Error migrating old avatar', e); + return Promise.resolve(); + } + } + }).then(() => { + const avatarsFiles = new Mongo.Collection('avatars.files'); + const avatarsChunks = new Mongo.Collection('avatars.chunks'); + avatarsFiles.rawCollection().drop(); + avatarsChunks.rawCollection().drop(); + RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStoreType'}); + RocketChat.models.Settings.remove({_id: 'Accounts_AvatarStorePath'}); + }); + }, 1000); + }); + } +}); From f9ad156717ca86719cb6edf29eebdb56b1bef206 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Wed, 5 Jul 2017 22:17:18 -0300 Subject: [PATCH 4/7] Remove unecessary comments --- server/startup/migrations/v099.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/startup/migrations/v099.js b/server/startup/migrations/v099.js index 43344c5e5d0..3d77e4a5e76 100644 --- a/server/startup/migrations/v099.js +++ b/server/startup/migrations/v099.js @@ -126,12 +126,6 @@ RocketChat.Migrations.add({ name: 'avatars' }); - // 1. Novo usuário, não executa migration - // 2. Antigo, migra de antes da 97, vai ter storage type - // 3. Executou a 97, não vai ter storeType - // 3.1 Se ainda tem dados no gridfs, manda ver - // 3.2 Se não tem, quebra e pede a variável OU forçar a reexecutar a 97??? - if (avatarStoreType == null) { const count = oldAvatarGridFS.countSync(); if (Match.test(count, Number) && count > 0) { From 47dad0da400dea17a24d4c2e21d029f350a15dec Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Wed, 5 Jul 2017 22:41:03 -0300 Subject: [PATCH 5/7] Fix the migration `rerun` command --- packages/rocketchat-migrations/migrations.js | 2 +- server/startup/migrations/v099.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-migrations/migrations.js b/packages/rocketchat-migrations/migrations.js index ee54c145e69..82885469da8 100644 --- a/packages/rocketchat-migrations/migrations.js +++ b/packages/rocketchat-migrations/migrations.js @@ -239,7 +239,7 @@ Migrations._migrateTo = function(version, rerun) { if (rerun) { log.info('Rerunning version ' + version); - migrate('up', version); + migrate('up', this._findIndexByVersion(version)); log.info('Finished migrating.'); unlock(); return true; diff --git a/server/startup/migrations/v099.js b/server/startup/migrations/v099.js index 3d77e4a5e76..5e933410332 100644 --- a/server/startup/migrations/v099.js +++ b/server/startup/migrations/v099.js @@ -133,7 +133,7 @@ RocketChat.Migrations.add({ } else if (Match.test(avatarsPath, String) && avatarsPath.length > 0) { avatarStoreType = 'FileSystem'; } else { - SystemLogger.error_box('Can\'t define the avatar\'s storage type.\nIf you have avatars missing and they was stored in your file system\nrun the process including the following environment variables: \n AVATARS_PATH=\'YOUR AVATAR\'S DIRECTORY\'\n MIGRATION_VERSION=98', 'WARNING'); + SystemLogger.error_box('Can\'t define the avatar\'s storage type.\nIf you have avatars missing and they was stored in your file system\nrun the process including the following environment variables: \n AVATARS_PATH=\'YOUR AVATAR\'S DIRECTORY\'\n MIGRATION_VERSION=99,rerun', 'WARNING'); return; } } From a7f2e6bcb55b2d7147bc606f6c8486257c719161 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 6 Jul 2017 10:23:39 -0300 Subject: [PATCH 6/7] Fix Emails in User Admin View --- packages/rocketchat-ui-admin/client/users/adminUsers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-ui-admin/client/users/adminUsers.js b/packages/rocketchat-ui-admin/client/users/adminUsers.js index 1562e6a095c..430f64a23ad 100644 --- a/packages/rocketchat-ui-admin/client/users/adminUsers.js +++ b/packages/rocketchat-ui-admin/client/users/adminUsers.js @@ -21,7 +21,7 @@ Template.adminUsers.helpers({ } }, emailAddress() { - return _.map(this.emails, function(e) { e.address; }).join(', '); + return _.map(this.emails, function(e) { return e.address; }).join(', '); }, flexData() { return { From 5cb30b479f37e3add3ce007dea5e37021491bec1 Mon Sep 17 00:00:00 2001 From: Flavio Grossi Date: Fri, 7 Jul 2017 19:22:13 +0200 Subject: [PATCH 7/7] [FIX] url click events in the cordova app open in external browser or not at all (#7205) * fix url click events in the cordova app fixes RocketChat/Rocket.Chat.Cordova#142 * make urls regexp case insensitive since mobile devices often autocapitalize them --- packages/rocketchat-ui-master/client/main.js | 2 +- .../rocketchat-ui/client/lib/cordova/urls.js | 4 ++-- packages/rocketchat-ui/client/views/app/room.js | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/rocketchat-ui-master/client/main.js b/packages/rocketchat-ui-master/client/main.js index 9e91eea3687..ad47ade5c67 100644 --- a/packages/rocketchat-ui-master/client/main.js +++ b/packages/rocketchat-ui-master/client/main.js @@ -184,7 +184,7 @@ Template.main.events({ }, 'touchmove'(e, t) { if (t.touchstartX != null) { - const [touch] = e.originalEvent.touches; + const touch = e.originalEvent.touches[0]; const diffX = touch.clientX - t.touchstartX; const diffY = touch.clientY - t.touchstartY; const absX = Math.abs(diffX); diff --git a/packages/rocketchat-ui/client/lib/cordova/urls.js b/packages/rocketchat-ui/client/lib/cordova/urls.js index a11c61d810b..ed6d9744e48 100644 --- a/packages/rocketchat-ui/client/lib/cordova/urls.js +++ b/packages/rocketchat-ui/client/lib/cordova/urls.js @@ -1,14 +1,14 @@ Meteor.startup(() => { if (!Meteor.isCordova) { return; } // Handle click events for all external URLs - $(document).on('deviceready', () => { + document.addEventListener('deviceready', () => { // const platform = device.platform.toLowerCase(); $(document).on('click', function(e) { const $link = $(e.target).closest('a[href]'); if (!($link.length > 0)) { return; } const url = $link.attr('href'); - if (/^https?:\/\/.+/.test(url) === true) { + if (/^https?:\/\/.+/i.test(url) === true) { window.open(url, '_system'); return e.preventDefault(); } diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 227956fdd7c..c066b0c600d 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -234,6 +234,8 @@ Template.room.helpers({ let isSocialSharingOpen = false; let touchMoved = false; +let lastTouchX = null; +let lastTouchY = null; Template.room.events({ 'click, touchend'(e, t) { @@ -249,6 +251,11 @@ Template.room.events({ }, 'touchstart .message'(e, t) { + const touches = e.originalEvent.touches; + if (touches && touches.length) { + lastTouchX = touches[0].pageX; + lastTouchY = touches[0].pagey; + } touchMoved = false; isSocialSharingOpen = false; if (e.originalEvent.touches.length !== 1) { @@ -343,7 +350,14 @@ Template.room.events({ }, 'touchmove .message'(e, t) { - touchMoved = true; + const touches = e.originalEvent.touches; + if (touches && touches.length) { + const deltaX = Math.abs(lastTouchX - touches[0].pageX); + const deltaY = Math.abs(lastTouchY - touches[0].pageY); + if (deltaX > 5 || deltaY > 5) { + touchMoved = true; + } + } return Meteor.clearTimeout(t.touchtime); },