message_media.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. '$window',
  30. function(webClientService: WebClientService,
  31. mediaboxService: MediaboxService,
  32. messageService: MessageService,
  33. $rootScope: ng.IRootScopeService,
  34. $mdDialog: ng.material.IDialogService,
  35. $timeout: ng.ITimeoutService,
  36. $log: ng.ILogService,
  37. $filter: ng.IFilterService,
  38. $window: ng.IWindowService) {
  39. return {
  40. restrict: 'EA',
  41. scope: {},
  42. bindToController: {
  43. message: '=eeeMessage',
  44. receiver: '=eeeReceiver',
  45. },
  46. controllerAs: 'ctrl',
  47. controller: [function() {
  48. this.type = this.message.type;
  49. this.downloading = false;
  50. this.thumbnailDownloading = false;
  51. this.downloaded = false;
  52. this.timeout = null as ng.IPromise<void>;
  53. this.uploading = this.message.temporaryId !== undefined
  54. && this.message.temporaryId !== null;
  55. this.isAnimGif = !this.uploading
  56. && (this.message as threema.Message).type === 'file'
  57. && (this.message as threema.Message).file.type === 'image/gif';
  58. // do not show thumbnail in file messages (except anim gif
  59. // if a thumbnail in file messages are available, the thumbnail
  60. // will be shown in the file circle
  61. this.showThumbnail = this.message.thumbnail !== undefined
  62. && ((this.message as threema.Message).type !== 'file'
  63. || this.isAnimGif);
  64. this.thumbnail = null;
  65. if (this.message.thumbnail !== undefined) {
  66. this.thumbnailStyle = {
  67. width: this.message.thumbnail.width + 'px',
  68. height: this.message.thumbnail.height + 'px' };
  69. }
  70. let loadingThumbnailTimeout = null;
  71. this.wasInView = false;
  72. this.thumbnailInView = (inView: boolean) => {
  73. if (this.message.thumbnail === undefined
  74. || this.wasInView === inView) {
  75. // do nothing
  76. return;
  77. }
  78. this.wasInView = inView;
  79. if (!inView) {
  80. $timeout.cancel(loadingThumbnailTimeout);
  81. this.thumbnailDownloading = false;
  82. this.thumbnail = null;
  83. } else {
  84. if (this.thumbnail === null) {
  85. if (this.message.thumbnail.img !== undefined) {
  86. this.thumbnail = $filter<any>('bufferToUrl')(this.message.thumbnail.img, 'image/png');
  87. return;
  88. } else {
  89. this.thumbnailDownloading = true;
  90. loadingThumbnailTimeout = $timeout(() => {
  91. webClientService.requestThumbnail(
  92. this.receiver,
  93. this.message).then((img) => {
  94. $timeout(() => {
  95. this.thumbnail = $filter<any>('bufferToUrl')(img, 'image/png');
  96. this.thumbnailDownloading = false;
  97. });
  98. });
  99. }, 1000);
  100. }
  101. }
  102. }
  103. };
  104. // For locations, retrieve the coordinates
  105. this.location = null;
  106. if (this.message.location !== undefined) {
  107. this.location = this.message.location;
  108. this.downloaded = true;
  109. }
  110. // Open map link in new window using mapLink-filter
  111. this.openMapLink = () => {
  112. $window.open($filter<any>('mapLink')(this.location), '_blank');
  113. };
  114. // Play a Audio file in a dialog
  115. this.playAudio = (buffer: ArrayBuffer) => {
  116. $mdDialog.show({
  117. controllerAs: 'ctrl',
  118. controller: function () {
  119. this.blobBuffer = buffer;
  120. this.cancel = () => {
  121. $mdDialog.cancel();
  122. };
  123. },
  124. template: `
  125. <md-dialog translate-attr="{'aria-label': 'messageTypes.AUDIO_MESSAGE'}">
  126. <md-toolbar>
  127. <div class="md-toolbar-tools">
  128. <h2 translate>messageTypes.AUDIO_MESSAGE</h2>
  129. </div>
  130. </md-toolbar>
  131. <md-dialog-content layout="row" layout-align="center">
  132. <audio
  133. controls
  134. autoplay ng-src="{{ ctrl.blobBuffer | bufferToUrl: 'audio/ogg' }}">
  135. Your browser does not support the <code>audio</code> element.
  136. </audio>
  137. </md-dialog-content>
  138. <md-dialog-actions layout="row" >
  139. <md-button ng-click="ctrl.cancel()">
  140. <span translate>common.OK</span>
  141. </md-button>
  142. </md-dialog-actions>
  143. </md-dialog>`,
  144. parent: angular.element(document.body),
  145. clickOutsideToClose: true,
  146. });
  147. };
  148. // Download function
  149. this.download = () => {
  150. if (this.downloading) {
  151. $log.debug('download already in progress...');
  152. return;
  153. }
  154. const message: threema.Message = this.message;
  155. const receiver: threema.Receiver = this.receiver;
  156. this.downloading = true;
  157. webClientService.requestBlob(message.id, receiver)
  158. .then((buffer: ArrayBuffer) => {
  159. $rootScope.$apply(() => {
  160. this.downloading = false;
  161. this.downloaded = true;
  162. switch (this.message.type) {
  163. case 'image':
  164. const caption = message.caption || '';
  165. mediaboxService.setMedia(buffer, messageService.getFileName(message), caption);
  166. break;
  167. case 'video':
  168. saveAs(new Blob([buffer]), messageService.getFileName(message));
  169. break;
  170. case 'file':
  171. if (this.message.file.type === 'image/gif') {
  172. // show inline
  173. this.blobBuffer = buffer;
  174. // hide thumbnail
  175. this.showThumbnail = false;
  176. } else {
  177. saveAs(new Blob([buffer]), messageService.getFileName(message));
  178. }
  179. break;
  180. case 'audio':
  181. // Show inline
  182. this.playAudio(buffer);
  183. break;
  184. default:
  185. $log.warn('Ignored download request for message type', this.message.type);
  186. }
  187. });
  188. })
  189. .catch((error) => {
  190. $log.error('error downloading blob ', error);
  191. this.downloading = false;
  192. this.downloaded = true;
  193. });
  194. };
  195. }],
  196. templateUrl: 'directives/message_media.html',
  197. };
  198. },
  199. ];