drag_file.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. /**
  18. * Allow to drag and drop elements, set class to parent object
  19. */
  20. export default [
  21. '$log',
  22. function($log: ng.ILogService) {
  23. return {
  24. restrict: 'EA',
  25. scope: {
  26. submit: '=',
  27. onUploading: '=',
  28. },
  29. link(scope: any, element) {
  30. // Logging
  31. const logTag = '[Directives.DragFile]';
  32. // Constants
  33. const DRAGOVER_CSS_CLASS = 'is-dragover';
  34. // Elements
  35. const overlay: any = angular.element(element[0]);
  36. const dragElement: any = angular.element(element[0]).parent('.drag-container');
  37. // Function to fetch file contents
  38. // Resolve to ArrayBuffer or reject to ErrorEvent.
  39. function fetchFileListContents(fileList: FileList): Promise<Map<File, ArrayBuffer>> {
  40. return new Promise((resolve) => {
  41. const buffers = new Map<File, ArrayBuffer>();
  42. const next = (file: File, res: ArrayBuffer | null, error?: FileReaderProgressEvent) => {
  43. buffers.set(file, res);
  44. if (buffers.size >= fileList.length) {
  45. resolve(buffers);
  46. }
  47. if (error !== undefined) {
  48. $log.error(logTag, 'Error:', error);
  49. }
  50. };
  51. for (let n = 0; n < fileList.length; n++) {
  52. const reader = new FileReader();
  53. const file = fileList.item(n);
  54. reader.onload = function(ev: FileReaderProgressEvent) {
  55. next(file, ev.target.result);
  56. };
  57. reader.onerror = function(ev: FileReaderProgressEvent) {
  58. // set a null object
  59. next(file, null, ev);
  60. };
  61. reader.onprogress = function(ev: FileReaderProgressEvent) {
  62. if (ev.lengthComputable) {
  63. const progress = ((ev.loaded / ev.total) * 100);
  64. scope.onUploading(true, progress, 100 / fileList.length * n);
  65. }
  66. };
  67. reader.readAsArrayBuffer(file);
  68. }
  69. });
  70. }
  71. function uploadFiles(fileList: FileList): void {
  72. scope.onUploading(true, 0, 0);
  73. fetchFileListContents(fileList).then((data: Map<File, ArrayBuffer>) => {
  74. const fileMessages = [];
  75. data.forEach((buffer, file) => {
  76. const fileMessageData: threema.FileMessageData = {
  77. name: file.name,
  78. fileType: file.type,
  79. size: file.size,
  80. data: buffer,
  81. };
  82. fileMessages.push(fileMessageData);
  83. });
  84. scope.submit('file', fileMessages);
  85. scope.onUploading(false);
  86. }).catch((ev: ErrorEvent) => {
  87. $log.error(logTag, 'Could not load file:', ev.message);
  88. });
  89. }
  90. // Handle the drop effect
  91. function onDrop(ev: DragEvent): void {
  92. ev.stopPropagation();
  93. ev.preventDefault();
  94. // simulate a leave to reset styles
  95. onDragLeave(ev);
  96. // Upload files
  97. uploadFiles(ev.dataTransfer.files);
  98. }
  99. // File is dragged over compose area
  100. function onDragOver(ev: DragEvent): void {
  101. ev.stopPropagation();
  102. ev.preventDefault();
  103. dragElement.addClass(DRAGOVER_CSS_CLASS);
  104. // composeArea.find('div').attr('placeholder', dragoverPlaceholder);
  105. }
  106. // File is dragged out of compose area
  107. function onDragLeave(ev: DragEvent): void {
  108. ev.stopPropagation();
  109. ev.preventDefault();
  110. dragElement.removeClass(DRAGOVER_CSS_CLASS);
  111. }
  112. // When switching chat, send stopTyping message
  113. scope.$on('$destroy', scope.stopTyping);
  114. // Handle dragover / dragleave events
  115. dragElement.on('dragover dragenter', onDragOver);
  116. overlay.on('dragleave dragend', onDragLeave);
  117. overlay.on('drop', onDrop);
  118. },
  119. template: `
  120. <div class="drag-file-overlay">
  121. <div>
  122. <span translate>messenger.COMPOSE_MESSAGE_DRAGOVER</span>
  123. </div>
  124. </div>`,
  125. };
  126. },
  127. ];