message_media.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /**
  2. * This file is part of Threema Web.
  3. *
  4. * Threema Web is free software: you can redistribute it and/or modify it
  5. * under the terms of the GNU Affero General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or (at
  7. * your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. import {MediaboxService} from '../services/mediabox';
  18. import {MessageService} from '../services/message';
  19. import {WebClientService} from '../services/webclient';
  20. export default [
  21. 'WebClientService',
  22. 'MediaboxService',
  23. 'MessageService',
  24. '$rootScope',
  25. '$mdDialog',
  26. '$timeout',
  27. '$log',
  28. '$filter',
  29. function(webClientService: WebClientService,
  30. mediaboxService: MediaboxService,
  31. messageService: MessageService,
  32. $rootScope: ng.IRootScopeService,
  33. $mdDialog: ng.material.IDialogService,
  34. $timeout: ng.ITimeoutService,
  35. $log: ng.ILogService,
  36. $filter: ng.IFilterService) {
  37. return {
  38. restrict: 'EA',
  39. scope: {},
  40. bindToController: {
  41. message: '=eeeMessage',
  42. receiver: '=eeeReceiver',
  43. },
  44. controllerAs: 'ctrl',
  45. controller: [function() {
  46. this.type = this.message.type;
  47. this.downloading = false;
  48. this.thumbnailDownloading = false;
  49. this.downloaded = false;
  50. this.timeout = null as ng.IPromise<void>;
  51. this.uploading = this.message.temporaryId !== undefined
  52. && this.message.temporaryId !== null;
  53. this.isAnimGif = !this.uploading
  54. && (this.message as threema.Message).type === 'file'
  55. && (this.message as threema.Message).file.type === 'image/gif';
  56. // do not show thumbnail in file messages (except anim gif
  57. // if a thumbnail in file messages are available, the thumbnail
  58. // will be shown in the file circle
  59. this.showThumbnail = this.message.thumbnail !== undefined
  60. && ((this.message as threema.Message).type !== 'file'
  61. || this.isAnimGif);
  62. this.thumbnail = null;
  63. if (this.message.thumbnail !== undefined) {
  64. this.thumbnailStyle = {
  65. width: this.message.thumbnail.width + 'px',
  66. height: this.message.thumbnail.height + 'px' };
  67. }
  68. let loadingThumbnailTimeout = null;
  69. this.wasInView = false;
  70. this.thumbnailInView = (inView: boolean) => {
  71. if (this.message.thumbnail === undefined
  72. || this.wasInView === inView) {
  73. // do nothing
  74. return;
  75. }
  76. this.wasInView = inView;
  77. if (!inView) {
  78. $timeout.cancel(loadingThumbnailTimeout);
  79. this.thumbnailDownloading = false;
  80. this.thumbnail = null;
  81. } else {
  82. if (this.thumbnail === null) {
  83. if (this.message.thumbnail.img !== undefined) {
  84. this.thumbnail = $filter<any>('bufferToUrl')(this.message.thumbnail.img, 'image/png');
  85. return;
  86. } else {
  87. this.thumbnailDownloading = true;
  88. loadingThumbnailTimeout = $timeout(() => {
  89. webClientService.requestThumbnail(
  90. this.receiver,
  91. this.message).then((img) => {
  92. $timeout(() => {
  93. this.thumbnail = $filter<any>('bufferToUrl')(img, 'image/png');
  94. this.thumbnailDownloading = false;
  95. });
  96. });
  97. }, 1000);
  98. }
  99. }
  100. }
  101. };
  102. // For locations, retrieve the coordinates
  103. this.location = null;
  104. if (this.message.location !== undefined) {
  105. this.location = this.message.location;
  106. this.downloaded = true;
  107. }
  108. // Play a Audio file in a dialog
  109. this.playAudio = (buffer: ArrayBuffer) => {
  110. $mdDialog.show({
  111. controllerAs: 'ctrl',
  112. controller: function () {
  113. this.blobBuffer = buffer;
  114. this.cancel = () => {
  115. $mdDialog.cancel();
  116. };
  117. },
  118. template: `
  119. <md-dialog translate-attr="{'aria-label': 'messageTypes.AUDIO_MESSAGE'}">
  120. <md-toolbar>
  121. <div class="md-toolbar-tools">
  122. <h2 translate>messageTypes.AUDIO_MESSAGE</h2>
  123. </div>
  124. </md-toolbar>
  125. <md-dialog-content layout="row" layout-align="center">
  126. <audio
  127. controls
  128. autoplay ng-src="{{ ctrl.blobBuffer | bufferToUrl: 'audio/ogg' }}">
  129. Your browser does not support the <code>audio</code> element.
  130. </audio>
  131. </md-dialog-content>
  132. <md-dialog-actions layout="row" >
  133. <md-button ng-click="ctrl.cancel()">
  134. <span translate>common.OK</span>
  135. </md-button>
  136. </md-dialog-actions>
  137. </md-dialog>`,
  138. parent: angular.element(document.body),
  139. clickOutsideToClose: true,
  140. });
  141. };
  142. // Download function
  143. this.download = () => {
  144. if (this.downloading) {
  145. $log.debug('download already in progress...');
  146. return;
  147. }
  148. const message: threema.Message = this.message;
  149. const receiver: threema.Receiver = this.receiver;
  150. this.downloading = true;
  151. webClientService.requestBlob(message.id, receiver)
  152. .then((buffer: ArrayBuffer) => {
  153. $rootScope.$apply(() => {
  154. this.downloading = false;
  155. this.downloaded = true;
  156. switch (this.message.type) {
  157. case 'image':
  158. let caption;
  159. if (message.caption) {
  160. caption = message.caption;
  161. } else {
  162. caption = messageService.getFileName(message);
  163. }
  164. mediaboxService.setMedia(buffer, caption);
  165. break;
  166. case 'video':
  167. saveAs(new Blob([buffer]), messageService.getFileName(message));
  168. break;
  169. case 'file':
  170. if (this.message.file.type === 'image/gif') {
  171. // show inline
  172. this.blobBuffer = buffer;
  173. // hide thumbnail
  174. this.showThumbnail = false;
  175. } else {
  176. saveAs(new Blob([buffer]), messageService.getFileName(message));
  177. }
  178. break;
  179. case 'audio':
  180. // Show inline
  181. this.playAudio(buffer);
  182. break;
  183. default:
  184. $log.warn('Ignored download request for message type', this.message.type);
  185. }
  186. });
  187. })
  188. .catch((error) => {
  189. $log.error('error downloading blob ', error);
  190. this.downloading = false;
  191. this.downloaded = true;
  192. });
  193. };
  194. }],
  195. templateUrl: 'directives/message_media.html',
  196. };
  197. },
  198. ];