Преглед на файлове

Merge pull request #218 from threema-ch/issue-90

Improve MIME type handling
Danilo Bargen преди 8 години
родител
ревизия
41232323c9

+ 2 - 0
karma.conf.js

@@ -5,9 +5,11 @@ module.exports = function(config) {
         files: [
             'node_modules/angular/angular.js',
             'node_modules/angular-mocks/angular-mocks.js',
+            'node_modules/angular-translate/dist/angular-translate.min.js',
             'dist/app.js',
             'tests/filters.js',
             'tests/service/message.js',
+            'tests/service/mime.js',
             'tests/service/qrcode.js',
             'tests/service/uri.js',
             'tests/service/webclient.js',

+ 1 - 1
public/i18n/de.json

@@ -178,7 +178,7 @@
         "NOTIFICATION_API_NOT_AVAILABLE": "Ihr Browser unterstützt keine Desktopbenachrichtigungen."
     },
     "mimeTypes": {
-        "android_apk": "Android-Paket",
+        "apk": "Android-Paket",
         "audio": "Audio-Datei",
         "certificate": "Digitales Zertifikat",
         "codes": "Programmcode",

+ 1 - 1
public/i18n/en.json

@@ -180,7 +180,7 @@
         "NOTIFICATION_API_NOT_AVAILABLE": "Your browser does not support desktop notifications."
     },
     "mimeTypes": {
-        "android_apk": "Android package",
+        "apk": "Android package",
         "audio": "Audio file",
         "certificate": "Digital certificate",
         "codes": "Source code",

+ 0 - 0
public/img/mime/ic_doc_contact_am.png → public/img/mime/ic_doc_contact.png


+ 0 - 0
public/img/mime/ic_doc_event_am.png → public/img/mime/ic_doc_event.png


BIN
public/img/mime/ic_doc_excel.png


BIN
public/img/mime/ic_doc_folder.png


+ 0 - 0
public/img/mime/ic_doc_generic_am.png → public/img/mime/ic_doc_generic.png


BIN
public/img/mime/ic_doc_parent.png


BIN
public/img/mime/ic_doc_powerpoint.png


+ 0 - 0
public/img/mime/ic_doc_spreadsheet_am.png → public/img/mime/ic_doc_spreadsheet.png


+ 0 - 0
public/img/mime/ic_doc_text_am.png → public/img/mime/ic_doc_text.png


+ 0 - 0
public/img/mime/ic_doc_video_am.png → public/img/mime/ic_doc_video.png


+ 8 - 4
src/directives/message_media.html

@@ -60,6 +60,7 @@
 
     <!-- Other file messages -->
     <div class="file-message" ng-if="ctrl.type === 'file' && !ctrl.isAnimGif" ng-click="ctrl.download()">
+
         <!-- Loading indicator -->
         <div class="circle"
              ng-class="{active: !ctrl.downloading}"
@@ -68,21 +69,24 @@
             <i class="material-icons md-24">file_download</i>
             <div class="loading" ng-class="{active: ctrl.downloading}"></div>
         </div>
-        <!-- Open Indicator -->
+
+        <!-- File type indicator -->
         <div class="circle"
              ng-if="ctrl.downloaded && ctrl.message.thumbnail.preview !== undefined"
              ng-style="{'background-image': 'url({{ctrl.message.thumbnail.preview | bufferToUrl: 'image/png'}})' }">
         </div>
-
         <div class="circle"
              ng-if="ctrl.downloaded && ctrl.message.thumbnail.preview == undefined">
-            <img ng-src="{{ ctrl.message.file.type | mimeType: false }}"/>
+            <img ng-src="{{ ctrl.message.file.type | mimeTypeIcon }}"/>
         </div>
+
+        <!-- File information -->
         <div class="info">
             <p>{{ctrl.message.file.name}}</p>
-            <p>{{ctrl.message.file.type | mimeType: true}}</p>
+            <p>{{ctrl.message.file.type | mimeTypeLabel}}</p>
             <p>{{ctrl.message.file.size | fileSize}}</p>
         </div>
+
     </div>
 </div>
 <!-- Ballot -->

+ 13 - 8
src/filters.ts

@@ -236,14 +236,19 @@ angular.module('3ema.filters', [])
         return (x + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]);
     };
 })
-.filter('mimeType', ['MimeService', function(mimeService: MimeService) {
-    return (mimeType: string, asText: boolean = true) => {
-        if (asText) {
-            return mimeService.getLabel(mimeType);
-        } else {
-            return mimeService.getIconUrl(mimeType);
-        }
-    };
+
+/**
+ * Return the MIME type label.
+ */
+.filter('mimeTypeLabel', ['MimeService', function(mimeService: MimeService) {
+    return (mimeType: string) => mimeService.getLabel(mimeType);
+}])
+
+/**
+ * Return the MIME type icon URL.
+ */
+.filter('mimeTypeIcon', ['MimeService', function(mimeService: MimeService) {
+    return (mimeType: string) => mimeService.getIconUrl(mimeType);
 }])
 
 /**

+ 7 - 5
src/partials/messenger.ts

@@ -383,11 +383,13 @@ class ConversationController {
                 case 'file':
                     // Determine file type
                     let showSendAsFileCheckbox = false;
-                    for (let msg of contents) {
-                        const mime = (msg as threema.FileMessageData).fileType;
-                        if (this.mimeService.isImage(mime)
-                            || this.mimeService.isAudio(mime)
-                            || this.mimeService.isVideo(mime)) {
+                    for (let msg of contents as threema.FileMessageData[]) {
+                        if (!msg.fileType) {
+                            msg.fileType = 'application/octet-stream';
+                        }
+                        if (this.mimeService.isImage(msg.fileType)
+                            || this.mimeService.isAudio(msg.fileType)
+                            || this.mimeService.isVideo(msg.fileType)) {
                             showSendAsFileCheckbox = true;
                             break;
                         }

+ 25 - 21
src/services/mime.ts

@@ -43,29 +43,35 @@ export class MimeService {
     }
 
     public getLabel(mimeType: string): string {
-        let key = this.getKey(mimeType);
-
+        const key = this.getKey(mimeType);
         if (key !== null) {
-            return this.$translate.instant('mimeTypes.' + this.getKey(mimeType));
+            return this.$translate.instant('mimeTypes.' + key);
         }
-
         return mimeType;
     }
 
     public getIconUrl(mimeType: string): string {
         let key = this.getKey(mimeType);
-        if (key === undefined || key.length === 0) {
-            key = 'generic_am';
+        if (key === undefined || key === null || key.length === 0) {
+            key = 'generic';
         }
         return 'img/mime/ic_doc_' + key + '.png';
     }
 
     private getKey(mimeType: string): string {
+        if (mimeType.startsWith('audio/')) {
+            return 'audio';
+        }
+        if (mimeType.startsWith('video/')) {
+            return 'video';
+        }
         switch (mimeType) {
             case 'application/vnd.android.package-archive':
                 return 'apk';
             case 'application/ogg':
             case 'application/x-flac':
+            case 'application/mpeg3':
+            case 'application/x-mpeg-3':
                 return 'audio';
             case 'application/pgp-keys':
             case 'application/pgp-signature':
@@ -128,10 +134,10 @@ export class MimeService {
                 return 'compressed';
             case 'text/x-vcard':
             case 'text/vcard':
-                return 'contact_am';
+                return 'contact';
             case 'text/calendar':
             case 'text/x-vcalendar':
-                return 'event_am';
+                return 'event';
             case 'application/x-font':
             case 'application/font-woff':
             case 'application/x-font-woff':
@@ -150,11 +156,15 @@ export class MimeService {
                 return 'image';
             case 'application/pdf':
                 return 'pdf';
+            case 'application/vnd.ms-powerpoint':
+            case 'application/vnd.oasis.opendocument.presentation':
+            case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
+            case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
+            case 'application/vnd.openxmlformats-officedocument.presentationml.template':
             case 'application/vnd.stardivision.impress':
             case 'application/vnd.sun.xml.impress':
             case 'application/vnd.sun.xml.impress.template':
             case 'application/x-kpresenter':
-            case 'application/vnd.oasis.opendocument.presentation':
                 return 'presentation';
             case 'application/vnd.oasis.opendocument.spreadsheet':
             case 'application/vnd.oasis.opendocument.spreadsheet-template':
@@ -162,7 +172,10 @@ export class MimeService {
             case 'application/vnd.sun.xml.calc':
             case 'application/vnd.sun.xml.calc.template':
             case 'application/x-kspread':
-                return 'spreadsheet_am';
+            case 'application/vnd.ms-excel':
+            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
+            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
+                return 'spreadsheet';
             case 'application/vnd.oasis.opendocument.text':
             case 'application/vnd.oasis.opendocument.text-master':
             case 'application/vnd.oasis.opendocument.text-template':
@@ -174,23 +187,14 @@ export class MimeService {
             case 'application/vnd.sun.xml.writer.template':
             case 'application/x-abiword':
             case 'application/x-kword':
-                return 'text_am';
+                return 'text';
             case 'application/x-quicktimeplayer':
             case 'application/x-shockwave-flash':
-                return 'video_am';
+                return 'video';
             case 'application/msword':
             case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
             case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template':
                 return 'word';
-            case 'application/vnd.ms-excel':
-            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
-            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
-                return 'excel';
-            case 'application/vnd.ms-powerpoint':
-            case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
-            case 'application/vnd.openxmlformats-officedocument.presentationml.template':
-            case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
-                return 'powerpoint';
             default:
                 return null;
         }

+ 33 - 0
tests/service/mime.js

@@ -0,0 +1,33 @@
+describe('MimeService', function() {
+
+    let $service;
+
+    beforeAll(() => window.onbeforeunload = () => 'Ignoring page reload request');
+
+    beforeEach(function() {
+
+        module('pascalprecht.translate');
+        module('3ema.services');
+
+        // Inject the service
+        inject(function(MimeService) {
+            $service = MimeService;
+        });
+
+    });
+
+    it('getLabel', () => {
+        expect($service.getLabel('application/pdf')).toEqual('mimeTypes.pdf');
+        expect($service.getLabel('application/vnd.android.package-archive')).toEqual('mimeTypes.apk');
+        expect($service.getLabel('audio/mpeg3')).toEqual('mimeTypes.audio');
+        expect($service.getLabel('audio/x-mpeg-3')).toEqual('mimeTypes.audio');
+        expect($service.getLabel('audio/foobar')).toEqual('mimeTypes.audio');
+    });
+
+    it('getIcon', () => {
+        expect($service.getIconUrl('application/pdf')).toEqual('img/mime/ic_doc_pdf.png');
+        expect($service.getIconUrl('application/vnd.android.package-archive')).toEqual('img/mime/ic_doc_apk.png');
+        expect($service.getIconUrl('video/baz')).toEqual('img/mime/ic_doc_video.png');
+    });
+
+});

+ 2 - 0
tests/testsuite.html

@@ -13,11 +13,13 @@
 
         <script src="../node_modules/angular/angular.js"></script>
         <script src="../node_modules/angular-mocks/angular-mocks.js"></script>
+        <script src="../node_modules/angular-translate/dist/angular-translate.min.js"></script>
 
         <script src="../dist/app.js"></script>
 
         <script src="filters.js"></script>
         <script src="service/message.js"></script>
+        <script src="service/mime.js"></script>
         <script src="service/qrcode.js"></script>
         <script src="service/uri.js"></script>
         <script src="service/webclient.js"></script>