From d167e18f0048344c54685a08c19113a84a995ed9 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Sat, 17 Oct 2015 01:58:34 +0200 Subject: PLT-616: Enable playing of animated GIF in thumbnails and preview --- web/react/components/file_attachment.jsx | 53 ++++++++++++++++++++++-- web/react/components/view_image.jsx | 70 +++++++++++++++++++++++++++++++- web/react/utils/utils.jsx | 1 + web/sass-files/sass/partials/_files.scss | 22 ++++++++++ web/sass-files/sass/partials/_modal.scss | 9 ++++ 5 files changed, 151 insertions(+), 4 deletions(-) (limited to 'web') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index c6dff6550..817834334 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -10,9 +10,10 @@ export default class FileAttachment extends React.Component { super(props); this.loadFiles = this.loadFiles.bind(this); + this.playGif = this.playGif.bind(this); this.canSetState = false; - this.state = {fileSize: -1}; + this.state = {fileSize: -1, mime: '', playing: false, loading: false}; } componentDidMount() { this.loadFiles(); @@ -93,6 +94,16 @@ export default class FileAttachment extends React.Component { return true; } + playGif(e, fileUrl) { + var img = new Image(); + + this.setState({loading: true}); + img.load(fileUrl); + img.onload = () => this.setState({playing: true, loading: false}); + img.onError = () => this.setState({loading: false}); + + e.stopPropagation(); + } render() { var filename = this.props.filename; @@ -100,6 +111,38 @@ export default class FileAttachment extends React.Component { var fileUrl = utils.getFileUrl(filename); var type = utils.getFileType(fileInfo.ext); + var playButton = ''; + var loadedFile = ''; + var loadingIndicator = ''; + if (this.state.mime === 'image/gif') { + playButton = ( +
this.playGif(e, fileUrl)} + > + {"►"} +
+ ); + } + if (this.state.playing) { + loadedFile = ( + + ); + playButton = ''; + } + if (this.state.loading) { + loadingIndicator = ( + + ); + playButton = ''; + } + var thumbnail; if (type === 'image') { thumbnail = ( @@ -107,7 +150,11 @@ export default class FileAttachment extends React.Component { ref={filename} className='post__load' style={{backgroundImage: 'url(/static/images/load.gif)'}} - /> + > + {loadingIndicator} + {playButton} + {loadedFile} + ); } else { thumbnail =
; @@ -119,7 +166,7 @@ export default class FileAttachment extends React.Component { filename, function success(data) { if (this.canSetState) { - this.setState({fileSize: parseInt(data.size, 10)}); + this.setState({fileSize: parseInt(data.size, 10), mime: data.mime}); } }.bind(this), function error() {} diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index 322e68c17..c8356b2fa 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -38,7 +38,10 @@ export default class ViewImageModal extends React.Component { progress: progress, images: {}, fileSizes: {}, - showFooter: false + fileMimes: {}, + showFooter: false, + isPlaying: {}, + isLoading: {} }; } handleNext(e) { @@ -122,6 +125,28 @@ export default class ViewImageModal extends React.Component { this.setState({loaded}); } } + playGif(e, filename, fileUrl) { + var isLoading = this.state.isLoading; + var isPlaying = this.state.isPlaying; + + isLoading[filename] = fileUrl; + this.setState({isLoading}); + + var img = new Image(); + img.load(fileUrl); + img.onload = () => { + delete isLoading[filename]; + isPlaying[filename] = fileUrl; + this.setState({isPlaying, isLoading}); + }; + img.onError = () => { + delete isLoading[filename]; + this.setState({isLoading}); + }; + + e.stopPropagation(); + e.preventDefault(); + } componentDidMount() { $(window).on('keyup', this.handleKeyPress); @@ -154,6 +179,10 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { + if (typeof this.state.isPlaying[filename] !== 'undefined') { + return this.state.isPlaying[filename]; + } + // This is a temporary patch to fix issue with old files using absolute paths if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; @@ -189,12 +218,51 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { + if (!(filename in this.state.fileMimes)) { + Client.getFileInfo( + filename, + (data) => { + if (this.canSetState) { + var fileMimes = this.state.fileMimes; + fileMimes[filename] = data.mime; + this.setState(fileMimes); + } + }, + () => {} + ); + } + + var playButton = ''; + if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading) && !(filename in this.state.isPlaying)) { + playButton = ( +
this.playGif(e, filename, fileUrl)} + > + {"►"} +
+ ); + } + + var loadingIndicator = ''; + if (this.state.isLoading[filename] === fileUrl) { + loadingIndicator = ( + + ); + playButton = ''; + } + // image files just show a preview of the file content = ( + {loadingIndicator} + {playButton} Date: Mon, 19 Oct 2015 21:15:10 +0200 Subject: Horizontally align images if image height > image width --- web/react/components/file_attachment.jsx | 18 +++++++++++++++--- web/sass-files/sass/partials/_files.scss | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'web') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index 817834334..afd4e32fe 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -94,16 +94,28 @@ export default class FileAttachment extends React.Component { return true; } - playGif(e, fileUrl) { + playGif(e, filename) { var img = new Image(); + var fileUrl = utils.getFileUrl(filename); this.setState({loading: true}); img.load(fileUrl); - img.onload = () => this.setState({playing: true, loading: false}); + img.onload = () => { + this.setState({playing: true, loading: false}); + + // keep displaying background image for a short moment while browser is + // loading gif, to prevent white background flashing through + setTimeout(() => this.removeBackgroundImage.bind(this)(filename), 100); + }; img.onError = () => this.setState({loading: false}); e.stopPropagation(); } + removeBackgroundImage(name) { + if (name in this.refs) { + $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial'); + } + } render() { var filename = this.props.filename; @@ -118,7 +130,7 @@ export default class FileAttachment extends React.Component { playButton = (
this.playGif(e, fileUrl)} + onClick={(e) => this.playGif(e, filename)} > {"►"}
diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 1d7754445..cb9ff6862 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -135,6 +135,7 @@ background-repeat: no-repeat; overflow: hidden; position: relative; + text-align: center; &.small { background-position: center; } -- cgit v1.2.3-1-g7c22 From cd5df514c7d3130c9765a8160dfd31abccb8b741 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Mon, 19 Oct 2015 22:30:00 +0200 Subject: Add stop button to stop animated gifs --- web/react/components/file_attachment.jsx | 101 ++++++++++++++++++++++--------- web/react/components/view_image.jsx | 45 ++++++++++---- web/react/utils/utils.jsx | 2 +- web/sass-files/sass/partials/_files.scss | 15 ++++- web/sass-files/sass/partials/_modal.scss | 4 ++ 5 files changed, 123 insertions(+), 44 deletions(-) (limited to 'web') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index afd4e32fe..a88731ca9 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -11,6 +11,8 @@ export default class FileAttachment extends React.Component { this.loadFiles = this.loadFiles.bind(this); this.playGif = this.playGif.bind(this); + this.stopGif = this.stopGif.bind(this); + this.addBackgroundImage = this.addBackgroundImage.bind(this); this.canSetState = false; this.state = {fileSize: -1, mime: '', playing: false, loading: false}; @@ -29,15 +31,9 @@ export default class FileAttachment extends React.Component { var filename = this.props.filename; if (filename) { - var fileInfo = utils.splitFileLocation(filename); + var fileInfo = this.getFileInfoFromName(filename); var type = utils.getFileType(fileInfo.ext); - // This is a temporary patch to fix issue with old files using absolute paths - if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { - fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; - } - fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; - if (type === 'image') { var self = this; // Need this reference since we use the given "this" $('').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) { @@ -59,11 +55,7 @@ export default class FileAttachment extends React.Component { $(imgDiv).addClass('normal'); } - var re1 = new RegExp(' ', 'g'); - var re2 = new RegExp('\\(', 'g'); - var re3 = new RegExp('\\)', 'g'); - var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); - $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); + self.addBackgroundImage(name, path); } }; }(fileInfo.path, filename)); @@ -111,6 +103,39 @@ export default class FileAttachment extends React.Component { e.stopPropagation(); } + stopGif(e, filename) { + this.setState({playing: false}); + this.addBackgroundImage(filename); + e.stopPropagation(); + } + getFileInfoFromName(name) { + var fileInfo = utils.splitFileLocation(name); + + // This is a temporary patch to fix issue with old files using absolute paths + if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { + fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; + } + fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; + + return fileInfo; + } + addBackgroundImage(name, path) { + var fileUrl = path; + + if (name in this.refs) { + if (!path) { + fileUrl = this.getFileInfoFromName(name).path; + } + + var imgDiv = ReactDOM.findDOMNode(this.refs[name]); + var re1 = new RegExp(' ', 'g'); + var re2 = new RegExp('\\(', 'g'); + var re3 = new RegExp('\\)', 'g'); + var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); + + $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); + } + } removeBackgroundImage(name) { if (name in this.refs) { $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial'); @@ -123,13 +148,13 @@ export default class FileAttachment extends React.Component { var fileUrl = utils.getFileUrl(filename); var type = utils.getFileType(fileInfo.ext); - var playButton = ''; + var playbackControls = ''; var loadedFile = ''; var loadingIndicator = ''; if (this.state.mime === 'image/gif') { - playButton = ( + playbackControls = (
this.playGif(e, filename)} > {"►"} @@ -143,7 +168,14 @@ export default class FileAttachment extends React.Component { src={fileUrl} /> ); - playButton = ''; + playbackControls = ( +
this.stopGif(e, filename)} + > + {"◼"} +
+ ); } if (this.state.loading) { loadingIndicator = ( @@ -152,22 +184,35 @@ export default class FileAttachment extends React.Component { src='/static/images/load.gif' /> ); - playButton = ''; + playbackControls = ''; } var thumbnail; if (type === 'image') { - thumbnail = ( -
- {loadingIndicator} - {playButton} - {loadedFile} -
- ); + if (this.state.playing) { + thumbnail = ( +
+ {playbackControls} + {loadedFile} +
+ ); + } else { + thumbnail = ( +
+ {loadingIndicator} + {playbackControls} + {loadedFile} +
+ ); + } } else { thumbnail =
; } diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index c8356b2fa..fbe32cb07 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -147,6 +147,14 @@ export default class ViewImageModal extends React.Component { e.stopPropagation(); e.preventDefault(); } + stopGif(e, filename) { + var isPlaying = this.state.isPlaying; + delete isPlaying[filename]; + this.setState({isPlaying}); + + e.stopPropagation(); + e.preventDefault(); + } componentDidMount() { $(window).on('keyup', this.handleKeyPress); @@ -179,7 +187,7 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { - if (typeof this.state.isPlaying[filename] !== 'undefined') { + if (filename in this.state.isPlaying) { return this.state.isPlaying[filename]; } @@ -232,16 +240,27 @@ export default class ViewImageModal extends React.Component { ); } - var playButton = ''; - if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading) && !(filename in this.state.isPlaying)) { - playButton = ( -
this.playGif(e, filename, fileUrl)} - > - {"►"} -
- ); + var playbackControls = ''; + if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading)) { + if (filename in this.state.isPlaying) { + playbackControls = ( +
this.stopGif(e, filename)} + > + {"◼"} +
+ ); + } else { + playbackControls = ( +
this.playGif(e, filename, fileUrl)} + > + {"►"} +
+ ); + } } var loadingIndicator = ''; @@ -252,7 +271,7 @@ export default class ViewImageModal extends React.Component { src='/static/images/load.gif' /> ); - playButton = ''; + playbackControls = ''; } // image files just show a preview of the file @@ -262,7 +281,7 @@ export default class ViewImageModal extends React.Component { target='_blank' > {loadingIndicator} - {playButton} + {playbackControls} Date: Mon, 19 Oct 2015 22:48:15 +0200 Subject: always show stop gif button on small screen sizes --- web/sass-files/sass/partials/_responsive.scss | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'web') diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 3bffe82a2..c8bb24f3a 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -758,6 +758,10 @@ .post-comments { padding: 9px 21px 10px 10px !important; } + + .post-image__column .post__image .file-playback-controls.stop, .image-wrapper > a .file-playback-controls.stop { + @include opacity(1); + } } @media screen and (max-width: 640px) { .access-history__table { -- cgit v1.2.3-1-g7c22 From b00697a5009b30eea7e34f340dfdf6b691c4087b Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Tue, 20 Oct 2015 20:42:02 +0200 Subject: apply portrait/landscape/quadrat format to playing gif to always display it in best possible way --- web/react/components/file_attachment.jsx | 20 +++++++++++++++++--- web/sass-files/sass/partials/_files.scss | 7 ++++++- 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'web') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index a88731ca9..a6957b06b 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -15,7 +15,7 @@ export default class FileAttachment extends React.Component { this.addBackgroundImage = this.addBackgroundImage.bind(this); this.canSetState = false; - this.state = {fileSize: -1, mime: '', playing: false, loading: false}; + this.state = {fileSize: -1, mime: '', playing: false, loading: false, format: ''}; } componentDidMount() { this.loadFiles(); @@ -93,7 +93,21 @@ export default class FileAttachment extends React.Component { this.setState({loading: true}); img.load(fileUrl); img.onload = () => { - this.setState({playing: true, loading: false}); + var state = {playing: true, loading: false}; + + switch (true) { + case img.width > img.height: + state.format = 'landscape'; + break; + case img.height > img.width: + state.format = 'portrait'; + break; + default: + state.format = 'quadrat'; + break; + } + + this.setState(state); // keep displaying background image for a short moment while browser is // loading gif, to prevent white background flashing through @@ -164,7 +178,7 @@ export default class FileAttachment extends React.Component { if (this.state.playing) { loadedFile = ( ); diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index e4d658230..d3ab3b9f8 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -150,8 +150,13 @@ margin-top: -16px; } .file__loaded { - height: 100px; max-width: initial; + &.landscape, &.quadrat { + height: 100px; + } + &.portrait { + width: 120px; + } } &:hover .file-playback-controls.stop { @include opacity(1); -- cgit v1.2.3-1-g7c22 From b9c637ec82d28684029573af119606575f4978d0 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Wed, 21 Oct 2015 18:15:04 +0200 Subject: Change stop symbol to another one since chrome-win can't display it --- web/react/components/file_attachment.jsx | 2 +- web/react/components/view_image.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'web') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index a6957b06b..57cccc4e0 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -187,7 +187,7 @@ export default class FileAttachment extends React.Component { className='file-playback-controls stop' onClick={(e) => this.stopGif(e, filename)} > - {"◼"} + {"■"}
); } diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index fbe32cb07..bea6ce7a5 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -248,7 +248,7 @@ export default class ViewImageModal extends React.Component { className='file-playback-controls stop' onClick={(e) => this.stopGif(e, filename)} > - {"◼"} + {"■"}
); } else { -- cgit v1.2.3-1-g7c22