diff --git a/.meteor/packages b/.meteor/packages index 0cbb854a2016..f490350b0352 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -190,3 +190,4 @@ rocketchat:version-check rocketchat:search chatpal:search +rocketchat:lazy-load diff --git a/.meteor/versions b/.meteor/versions index d51b6678e431..c71a541b67d8 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -174,6 +174,7 @@ rocketchat:internal-hubot@0.0.1 rocketchat:irc@0.0.2 rocketchat:issuelinks@0.0.1 rocketchat:katex@0.0.1 +rocketchat:lazy-load@0.0.1 rocketchat:ldap@0.0.1 rocketchat:lib@0.0.1 rocketchat:livechat@0.0.1 diff --git a/packages/rocketchat-lazy-load/client/index.js b/packages/rocketchat-lazy-load/client/index.js new file mode 100644 index 000000000000..9b525117e01b --- /dev/null +++ b/packages/rocketchat-lazy-load/client/index.js @@ -0,0 +1,58 @@ +import _ from 'underscore'; +import './lazyloadImage'; +export const fixCordova = function(url) { + if (url && url.indexOf('data:image') === 0) { + return url; + } + if (Meteor.isCordova && (url && url[0] === '/')) { + url = Meteor.absoluteUrl().replace(/\/$/, '') + url; + const query = `rc_uid=${ Meteor.userId() }&rc_token=${ Meteor._localStorage.getItem( + 'Meteor.loginToken' + ) }`; + if (url.indexOf('?') === -1) { + url = `${ url }?${ query }`; + } else { + url = `${ url }&${ query }`; + } + } + if (Meteor.settings['public'].sandstorm || url.match(/^(https?:)?\/\//i)) { + return url; + } else if (navigator.userAgent.indexOf('Electron') > -1) { + return __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + url; + } else { + return Meteor.absoluteUrl().replace(/\/$/, '') + url; + } +}; + +const loadImage = el => { + const img = new Image(); + const src = el.getAttribute('data-src'); + el.className = el.className.replace('lazy-img', ''); + img.onload = function() { + el.src = src; + el.removeAttribute('data-src'); + }; + img.src = fixCordova(src); +}; + +const isVisible = el => { + requestAnimationFrame(() => { + const rect = el.getBoundingClientRect(); + if (rect.top >= -100 && rect.left >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight)) { + return loadImage(el); + } + }); + +}; + +window.addEventListener('resize', window.lazyloadtick); + +export const lazyloadtick = _.debounce(() => { + [...document.querySelectorAll('.lazy-img[data-src]')] + + .forEach(isVisible); +}, 500); + +window.lazyloadtick = lazyloadtick; + +export const addImage = el => isVisible(el); diff --git a/packages/rocketchat-lazy-load/client/lazyloadImage.html b/packages/rocketchat-lazy-load/client/lazyloadImage.html new file mode 100644 index 000000000000..c88c6b927623 --- /dev/null +++ b/packages/rocketchat-lazy-load/client/lazyloadImage.html @@ -0,0 +1,4 @@ + diff --git a/packages/rocketchat-lazy-load/client/lazyloadImage.js b/packages/rocketchat-lazy-load/client/lazyloadImage.js new file mode 100644 index 000000000000..c8d6a637db0e --- /dev/null +++ b/packages/rocketchat-lazy-load/client/lazyloadImage.js @@ -0,0 +1,21 @@ +import './lazyloadImage.html'; +import { addImage, fixCordova } from './'; + +Template.lazyloadImage.helpers({ + lazy() { + const { preview, src, placeholder } = this; + + if (!preview && !placeholder) { + return fixCordova(src); + } + return `data:image/png;base64,${ preview || 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8+/u3PQAJJAM0dIyWdgAAAABJRU5ErkJggg==' }`; + } +}); + +Template.lazyloadImage.onCreated(function() { + const element = Template.instance().firstNode; + if (!element) { + return; + } + addImage(element); +}); diff --git a/packages/rocketchat-lazy-load/package.js b/packages/rocketchat-lazy-load/package.js new file mode 100644 index 000000000000..d65e3f99cb54 --- /dev/null +++ b/packages/rocketchat-lazy-load/package.js @@ -0,0 +1,16 @@ +Package.describe({ + name: 'rocketchat:lazy-load', + version: '0.0.1', + summary: 'Lazy load image', + git: '' +}); + +Package.onUse(function(api) { + api.use([ + 'ecmascript', + 'templating', + 'rocketchat:lib' + ]); + + api.mainModule('client/index.js', 'client'); +}); diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.html b/packages/rocketchat-message-attachments/client/messageAttachment.html index 4c3879a32c01..6bc61ddd0643 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.html +++ b/packages/rocketchat-message-attachments/client/messageAttachment.html @@ -66,9 +66,7 @@
{{#if loadImage}}
-
- -
+ {{> lazyloadImage src=image_url preview=image_preview height=(getImageHeight image_dimensions.height) class="lazy-img gallery-item" title=title description=description}} {{#if labels}}
{{#each labels}} diff --git a/packages/rocketchat-message-attachments/client/messageAttachment.js b/packages/rocketchat-message-attachments/client/messageAttachment.js index 69680886f5c5..37acc16c00ba 100644 --- a/packages/rocketchat-message-attachments/client/messageAttachment.js +++ b/packages/rocketchat-message-attachments/client/messageAttachment.js @@ -1,30 +1,11 @@ import moment from 'moment'; +import { fixCordova } from 'meteor/rocketchat:lazy-load'; const colors = { good: '#35AC19', warning: '#FCB316', danger: '#D30230' }; -const fixCordova = function(url) { - if (url && url.indexOf('data:image') === 0) { - return url; - } - if (Meteor.isCordova && (url && url[0] === '/')) { - url = Meteor.absoluteUrl().replace(/\/$/, '') + url; - const query = `rc_uid=${ Meteor.userId() }&rc_token=${ Meteor._localStorage.getItem('Meteor.loginToken') }`; - if (url.indexOf('?') === -1) { - url = `${ url }?${ query }`; - } else { - url = `${ url }&${ query }`; - } - } - if (Meteor.settings['public'].sandstorm || url.match(/^(https?:)?\/\//i)) { - return url; - } else if (navigator.userAgent.indexOf('Electron') > -1) { - return __meteor_runtime_config__.ROOT_URL_PATH_PREFIX + url; - } else { - return Meteor.absoluteUrl().replace(/\/$/, '') + url; - } -}; + /*globals renderMessageBody*/ Template.messageAttachment.helpers({ fixCordova, @@ -34,8 +15,8 @@ Template.messageAttachment.helpers({ }); }, loadImage() { - const user = Meteor.user(); if (this.downloadImages !== true) { + const user = RocketChat.models.Users.findOne({_id: Meteor.userId()}, {fields: {'settings.autoImageLoad' : 1}}); if (RocketChat.getUserPreference(user, 'autoImageLoad') === false) { return false; } diff --git a/packages/rocketchat-message-attachments/package.js b/packages/rocketchat-message-attachments/package.js index eaaa2c47f6fb..571487cf2b42 100644 --- a/packages/rocketchat-message-attachments/package.js +++ b/packages/rocketchat-message-attachments/package.js @@ -9,7 +9,8 @@ Package.onUse(function(api) { api.use([ 'templating', 'ecmascript', - 'rocketchat:lib' + 'rocketchat:lib', + 'rocketchat:lazy-load' ]); api.addFiles('client/messageAttachment.html', 'client'); diff --git a/packages/rocketchat-theme/client/imports/general/base_old.css b/packages/rocketchat-theme/client/imports/general/base_old.css index 48e69f558aaf..208c7b6f46ed 100644 --- a/packages/rocketchat-theme/client/imports/general/base_old.css +++ b/packages/rocketchat-theme/client/imports/general/base_old.css @@ -3167,8 +3167,6 @@ max-height: 200px; cursor: pointer; - - opacity: 0; } } diff --git a/packages/rocketchat-ui-account/client/avatar/avatar.html b/packages/rocketchat-ui-account/client/avatar/avatar.html index 70781f54c7b5..de8f529657fe 100644 --- a/packages/rocketchat-ui-account/client/avatar/avatar.html +++ b/packages/rocketchat-ui-account/client/avatar/avatar.html @@ -1,5 +1,9 @@ diff --git a/packages/rocketchat-ui-account/client/avatar/avatar.js b/packages/rocketchat-ui-account/client/avatar/avatar.js index 555bf769cd7e..27e00392a8cb 100644 --- a/packages/rocketchat-ui-account/client/avatar/avatar.js +++ b/packages/rocketchat-ui-account/client/avatar/avatar.js @@ -1,5 +1,5 @@ Template.avatar.helpers({ - imageUrl() { + src() { let {url} = Template.instance().data; if (!url) { let username = this.username; @@ -18,6 +18,6 @@ Template.avatar.helpers({ url = getAvatarUrlFromUsername(username); } - return `background-image:url(${ url });`; + return url; } }); diff --git a/packages/rocketchat-ui-account/package.js b/packages/rocketchat-ui-account/package.js index 2d8ac01d4fa1..fbdc651699aa 100644 --- a/packages/rocketchat-ui-account/package.js +++ b/packages/rocketchat-ui-account/package.js @@ -15,7 +15,8 @@ Package.onUse(function(api) { 'ecmascript', 'templating', 'rocketchat:lib', - 'sha' + 'sha', + 'rocketchat:lazy-load' ]); api.addFiles('client/account.html', 'client'); diff --git a/packages/rocketchat-ui-sidenav/client/sideNav.js b/packages/rocketchat-ui-sidenav/client/sideNav.js index f360e188322e..fb5ba7d0c720 100644 --- a/packages/rocketchat-ui-sidenav/client/sideNav.js +++ b/packages/rocketchat-ui-sidenav/client/sideNav.js @@ -1,3 +1,5 @@ +import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; + /* globals menu*/ Template.sideNav.helpers({ @@ -51,6 +53,7 @@ Template.sideNav.events({ }, 'scroll .rooms-list'() { + lazyloadtick(); return menu.updateUnreadBars(); }, @@ -62,7 +65,7 @@ Template.sideNav.events({ Template.sideNav.onRendered(function() { SideNav.init(); menu.init(); - + lazyloadtick(); const first_channel_login = RocketChat.settings.get('First_Channel_After_Login'); const room = RocketChat.roomTypes.findRoom('c', first_channel_login, Meteor.userId()); if (room !== undefined && room._id !== '') { diff --git a/packages/rocketchat-ui-sidenav/client/sidebarItem.html b/packages/rocketchat-ui-sidenav/client/sidebarItem.html index b527528cf394..54404f12dbe9 100644 --- a/packages/rocketchat-ui-sidenav/client/sidebarItem.html +++ b/packages/rocketchat-ui-sidenav/client/sidebarItem.html @@ -11,7 +11,7 @@ {{/if}} {{else}} {{/if}}
diff --git a/packages/rocketchat-ui-sidenav/package.js b/packages/rocketchat-ui-sidenav/package.js index 752ba50ba538..4345f9bb5ec0 100644 --- a/packages/rocketchat-ui-sidenav/package.js +++ b/packages/rocketchat-ui-sidenav/package.js @@ -15,7 +15,8 @@ Package.onUse(function(api) { 'ecmascript', 'templating', 'rocketchat:lib', - 'rocketchat:ui' + 'rocketchat:ui', + 'rocketchat:lazy-load' ]); api.addFiles('client/createCombinedFlex.html', 'client'); diff --git a/packages/rocketchat-ui/client/views/app/room.js b/packages/rocketchat-ui/client/views/app/room.js index 7fe7de467fae..7fbdfc3fe6ab 100644 --- a/packages/rocketchat-ui/client/views/app/room.js +++ b/packages/rocketchat-ui/client/views/app/room.js @@ -6,6 +6,8 @@ import moment from 'moment'; import mime from 'mime-type/with-db'; import Clipboard from 'clipboard'; +import { lazyloadtick } from 'meteor/rocketchat:lazy-load'; + window.chatMessages = window.chatMessages || {}; const isSubscribed = _id => ChatSubscription.find({ rid: _id }).count() > 0; @@ -536,6 +538,9 @@ Template.room.events({ }, 'scroll .wrapper': _.throttle(function(e, t) { + + lazyloadtick(); + const $roomLeader = $('.room-leader'); if ($roomLeader.length) { if (e.target.scrollTop < lastScrollTop) { @@ -739,6 +744,9 @@ Template.room.events({ Template.room.onCreated(function() { // this.scrollOnBottom = true // this.typing = new msgTyping this.data._id + + lazyloadtick(); + this.showUsersOffline = new ReactiveVar(false); this.atBottom = FlowRouter.getQueryParam('msg') ? false : true; this.unreadCount = new ReactiveVar(0); @@ -898,6 +906,8 @@ Template.room.onRendered(function() { if (template.atBottom === true && template.isAtBottom() !== true) { template.sendToBottom(); } + + lazyloadtick(); }; template.sendToBottomIfNecessaryDebounced = _.debounce(template.sendToBottomIfNecessary, 10); diff --git a/packages/rocketchat-ui/package.js b/packages/rocketchat-ui/package.js index 68782079277d..343cd8152feb 100644 --- a/packages/rocketchat-ui/package.js +++ b/packages/rocketchat-ui/package.js @@ -23,7 +23,8 @@ Package.onUse(function(api) { 'rocketchat:lib', 'rocketchat:ui-master', 'raix:push', - 'raix:ui-dropped-event' + 'raix:ui-dropped-event', + 'rocketchat:lazy-load' ]); api.use('kadira:flow-router', 'client');