Ver código fonte

Append message id to downloading files

Fixes #61
Silly 8 anos atrás
pai
commit
b109ed2eff

+ 1 - 0
karma.conf.js

@@ -8,6 +8,7 @@ module.exports = function(config) {
             'dist/app.js',
             'tests/filters.js',
             'tests/qrcode.js',
+            'tests/service/message.js'
         ],
         customLaunchers: {
             Chromium_ci_gitlab: {

+ 1 - 8
src/directives/message.ts

@@ -109,17 +109,10 @@ export default [
 
                                 switch (this.message.type) {
                                     case 'image':
-                                        saveAs(new Blob([buffer]), 'image.jpg');
-                                        break;
                                     case 'video':
-                                        saveAs(new Blob([buffer]), 'video.mpg');
-                                        break;
                                     case 'file':
-                                        saveAs(new Blob([buffer]), this.message.file.name);
-                                        break;
                                     case 'audio':
-                                        // Show inline
-                                        saveAs(new Blob([buffer]), 'audio.ogg');
+                                        saveAs(new Blob([buffer]), messageService.getFileName(this.message));
                                         break;
                                     default:
                                         $log.warn('Ignored download request for message type', this.message.type);

+ 5 - 5
src/directives/message_media.ts

@@ -17,12 +17,14 @@
 
 export default [
     'WebClientService',
+    'MessageService',
     '$rootScope',
     '$mdDialog',
     '$timeout',
     '$log',
     '$filter',
-    function(webClientService: threema.WebClientService, $rootScope: ng.IRootScopeService,
+    function(webClientService: threema.WebClientService, messageService: threema.MessageService,
+             $rootScope: ng.IRootScopeService,
              $mdDialog: ng.material.IDialogService,
              $timeout: ng.ITimeoutService, $log: ng.ILogService,
              $filter: ng.IFilterService) {
@@ -156,10 +158,8 @@ export default [
 
                                 switch (this.message.type) {
                                     case 'image':
-                                        saveAs(new Blob([buffer]), 'image.jpg');
-                                        break;
                                     case 'video':
-                                        saveAs(new Blob([buffer]), 'video.mpg');
+                                        saveAs(new Blob([buffer]), messageService.getFileName(message));
                                         break;
                                     case 'file':
                                         if (this.message.file.type === 'image/gif') {
@@ -168,7 +168,7 @@ export default [
                                             // hide thumbnail
                                             this.showThumbnail = false;
                                         } else {
-                                            saveAs(new Blob([buffer]), this.message.file.name);
+                                            saveAs(new Blob([buffer]), messageService.getFileName(message));
                                         }
                                         break;
                                     case 'audio':

+ 47 - 4
src/services/message.ts

@@ -38,7 +38,7 @@ export class MessageService implements threema.MessageService {
     public getAccess(message: threema.Message, receiver: threema.Receiver): MessageAccess  {
         let access = new MessageAccess();
 
-        if (message !== undefined || receiver !== undefined || message.temporaryId === undefined) {
+        if (message !== undefined && receiver !== undefined && message.temporaryId === undefined) {
             access.quote =  (message.type === 'text')
                 || (message.type === 'location')
                 || (message.caption !== undefined);
@@ -69,7 +69,7 @@ export class MessageService implements threema.MessageService {
     }
 
     public showStatusIcon(message: threema.Message, receiver: threema.Receiver): boolean {
-        if (message !== null) {
+        if (message !== null && receiver !== null) {
 
             let messageState = message.state;
             // MessageState messageState = messageModel.getState();
@@ -77,8 +77,7 @@ export class MessageService implements threema.MessageService {
             // group message/distribution list message icons only on pending or failing states
             switch (receiver.type) {
                 case 'group':
-
-                    if (message.isOutbox && message.temporaryId === undefined && message.temporaryId === null) {
+                    if (message.isOutbox && (message.temporaryId === undefined || message.temporaryId === null)) {
                         return messageState === 'send-failed'
                             || messageState === 'sending'
                             || (messageState === 'pending' && message.type !== 'ballot');
@@ -96,6 +95,7 @@ export class MessageService implements threema.MessageService {
                             || messageState === 'sending'
                             || messageState === 'pending';
                     }
+
                     return true;
                 default:
                     return false;
@@ -104,6 +104,49 @@ export class MessageService implements threema.MessageService {
         return false;
     }
 
+    /**
+     * return the filename of a message (image, audio, file)
+     * used for downloads
+     * @param message
+     * @returns filename string or null
+     */
+    public getFileName(message: threema.Message): string {
+        if (message === undefined
+            || message === null) {
+            return null;
+        }
+
+        let getFileName = (prefix: string, postfix?: string): string => {
+            if (message.id === undefined) {
+                this.$log.warn('missing id on message model');
+                return null;
+            }
+            return prefix
+                + '-' + message.id
+                + (postfix !== undefined ? '.' + postfix : '');
+        };
+
+        switch (message.type) {
+            case 'image':
+                return getFileName('image', 'jpg');
+            case 'video':
+                return getFileName('video', 'mpg');
+            case 'file':
+                if (message.file !== undefined) {
+                    return message.file.name;
+                }
+
+                // should not happen
+                this.$log.warn('file message without file object', message.id);
+                return getFileName('file');
+            case 'audio':
+                return getFileName('audio', 'mp4');
+            default:
+                // ignore file types without a read file
+                return null;
+        }
+    }
+
     /**
      * Create a message object with a temporaryId
      */

+ 1 - 0
src/threema.d.ts

@@ -334,6 +334,7 @@ declare namespace threema {
         getAccess(message: Message, receiver: Receiver): MessageAccess;
         createTemporary(receiver: Receiver, type: string, messageData: MessageData): Message;
         showStatusIcon(message: Message, receiver: Receiver): boolean;
+        getFileName(message: Message): string;
     }
 
     interface MessageAccess {

+ 316 - 0
tests/service/message.js

@@ -0,0 +1,316 @@
+describe('MessageService', function() {
+
+    let messageService;
+
+    beforeEach(function() {
+
+        // load threema services
+        module('3ema.services');
+
+        // Inject the MessageService
+        inject(function(MessageService) {
+            messageService = MessageService;
+        });
+
+    });
+
+    describe ('getAccess', () => {
+        let test = (m, r) => {
+            return expect(messageService.getAccess(m, r));
+        };
+        it('invalid arguments', () => {
+            test().toEqual(jasmine.objectContaining({
+                quote: false,
+                ack: false,
+                dec: false,
+                delete: false,
+                download: false,
+                copy: false,
+            }));
+        })
+
+        const testCases = [['contact', {id: 'ECHOECHO', type: 'contact'}, true],
+            ['gateway contact', {id: '*THREEMA', type: 'contact'}, true],
+            ['group', {id: 1, type: 'group'}, false],
+            ['distributionList', {id: 1, type: 'distributionList'}, false]]
+
+        testCases.forEach((testData) => {
+
+            let type = testData[0];
+            let receiver = testData[1];
+            let canAckDec = testData[2];
+
+            describe(type, () => {
+                it('text messages  ', () => {
+                    test({isOutbox: false, type: 'text'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: true && canAckDec,
+                            dec: true && canAckDec,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+                    test({isOutbox: true, type: 'text'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: false,
+                            dec: false,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+                    test({isOutbox: false, type: 'text', state: 'user-ack'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: false,
+                            dec: true && canAckDec,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+
+                    test({isOutbox: false, type: 'text', state: 'user-dec'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: true && canAckDec,
+                            dec: false,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+                });
+
+                it('location messages  ', () => {
+                    test({isOutbox: false, type: 'text'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: true && canAckDec,
+                            dec: true && canAckDec,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+                    test({isOutbox: true, type: 'text'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: false,
+                            dec: false,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+                    test({isOutbox: false, type: 'text', state: 'user-ack'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: false,
+                            dec: true && canAckDec,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+
+
+                    test({isOutbox: false, type: 'text', state: 'user-dec'}, receiver)
+                        .toEqual(jasmine.objectContaining({
+                            quote: true,
+                            ack: true && canAckDec,
+                            dec: false,
+                            delete: true,
+                            download: false,
+                            copy: false,
+                        }));
+                });
+
+                ['image', 'video', 'audio', 'file'].forEach((type) => {
+                    it('inbox ' + type, () => {
+                        test({isOutbox: false, type: type}, receiver)
+                            .toEqual(jasmine.objectContaining({
+                                quote: false,
+                                ack: true && canAckDec,
+                                dec: true && canAckDec,
+                                delete: true,
+                                download: true,
+                                copy: false,
+                            }));
+                    });
+
+                    it('inbox (caption) ' + type, () => {
+                        test({isOutbox: false, type: type, caption: 'test'}, receiver)
+                            .toEqual(jasmine.objectContaining({
+                                quote: true,
+                                ack: true && canAckDec,
+                                dec: true && canAckDec,
+                                delete: true,
+                                download: true,
+                                copy: false,
+                            }));
+                    });
+
+                    it('outbox ' + type, () => {
+                        test({isOutbox: false, type: type}, receiver)
+                            .toEqual(jasmine.objectContaining({
+                                quote: false,
+                                ack: true && canAckDec,
+                                dec: true && canAckDec,
+                                delete: true,
+                                download: true,
+                                copy: false,
+                            }));
+                    });
+
+
+                    it('outbox (caption) ' + type, () => {
+                        test({isOutbox: false, type: type, caption: 'test'}, receiver)
+                            .toEqual(jasmine.objectContaining({
+                                quote: true,
+                                ack: true && canAckDec,
+                                dec: true && canAckDec,
+                                delete: true,
+                                download: true,
+                                copy: false,
+                            }));
+                    });
+                });
+            });
+        });
+    });
+
+    describe ('showStatusIcon', () => {
+        let test = (m, r) => {
+            return expect(messageService.showStatusIcon(m, r));
+        };
+
+        it ('invalid arguments', () => {
+            test(null, null).toEqual(false);
+            test({}, null).toEqual(false);
+            test(null, {}).toEqual(false);
+            test({}, {}).toEqual(false);
+        });
+
+
+        let testAll = (receiver, outboxTrue, outboxFalse, inboxTrue, inboxFalse) => {
+            let testDirection = (outbox, t, f) => {
+                let testDirections = (states, toBe) => {
+                    states.forEach((state) => {
+                        it(state, () => {
+                            test({isOutbox: outbox, state: state}, receiver).toBe(toBe);
+                        });
+                    });
+                };
+                testDirections(t, true);
+                testDirections(f, false);
+            };
+            describe ('outbox', () => {
+                testDirection(true, outboxTrue, outboxFalse);
+            });
+
+            describe ('inbox', () => {
+                testDirection(false, inboxTrue, inboxFalse);
+            });
+        };
+
+        describe ('normal contact', () => {
+            testAll({id: 'ECHOECHO', type: 'contact'},
+                ['delivered', 'read', 'send-failed', 'sent', 'pending', 'sending', 'user-ack', 'user-dec'],
+                [],
+                ['user-ack', 'user-dec'],
+                ['delivered', 'read', 'send-failed', 'sent', 'pending', 'sending']
+            );
+        });
+
+        describe ('gateway contact', () => {
+            testAll({id: '*THREEMA', type: 'contact'},
+                ['send-failed', 'pending', 'sending'],
+                ['delivered', 'read', 'sent', 'user-ack', 'user-dec'],
+                ['user-ack', 'user-dec'],
+                ['delivered', 'read', 'send-failed', 'sent', 'pending', 'sending']
+            );
+        });
+
+        describe ('Group', () => {
+            testAll({id: 1, type: 'group'},
+                ['send-failed', 'pending', 'sending'],
+                ['delivered', 'read', 'sent', 'user-ack', 'user-dec'],
+                [],
+                ['delivered', 'read', 'send-failed', 'sent', 'pending', 'sending', 'user-ack', 'user-dec']
+            );
+        });
+
+        describe ('Distribution List', () => {
+            testAll({id: 1, type: 'distributionList'},
+                [],
+                ['send-failed', 'pending', 'sending', 'delivered', 'read', 'sent', 'user-ack', 'user-dec'],
+                [],
+                ['delivered', 'read', 'send-failed', 'sent', 'pending', 'sending', 'user-ack', 'user-dec']
+            );
+        });
+    });
+
+    describe('getFileName', () => {
+        it('image', () => {
+            expect(messageService.getFileName({type: 'image', id: '123'}))
+                .toEqual('image-123.jpg');
+        });
+
+        it('audio', () => {
+            expect(messageService.getFileName({type: 'audio', id: '123'}))
+                .toEqual('audio-123.mp4');
+        });
+
+        it('video', () => {
+            expect(messageService.getFileName({type: 'video', id: '123'}))
+                .toEqual('video-123.mpg');
+        });
+
+        it('file', () => {
+            expect(messageService.getFileName({type: 'file', id: '123', file: {
+                type: 'xyz',
+                name: 'the-quick-fox.xyz'
+            }})).toEqual('the-quick-fox.xyz');
+        });
+
+        it('file without file object', () => {
+            expect(messageService.getFileName({type: 'file', id: '123'}))
+                .toEqual('file-123');
+        });
+
+        it('text', () => {
+            expect(messageService.getFileName({type: 'text', id: '123'}))
+                .toEqual(null);
+        });
+
+        it('no file types', () => {
+            ['text', 'location', 'status', 'ballot'].forEach((type) => {
+                expect(messageService.getFileName({type: type, id: '123'}))
+                    .toEqual(null);
+            });
+        });
+
+        it('no type', () => {
+            expect(messageService.getFileName({id: 1}))
+                .toEqual(null);
+        });
+        it('no id', () => {
+            expect(messageService.getFileName({type: 'image'}))
+                .toEqual(null);
+        });
+
+        it('no type', () => {
+            expect(messageService.getFileName({id: 1}))
+                .toEqual(null);
+        });
+        it('no message', () => {
+            expect(messageService.getFileName(null))
+                .toEqual(null);
+            expect(messageService.getFileName())
+                .toEqual(null);
+        });
+
+    });
+});

+ 1 - 0
tests/testsuite.html

@@ -18,6 +18,7 @@
 
         <script src="filters.js"></script>
         <script src="qrcode.js"></script>
+        <script src="service/message.js"></script>
     </head>
     <body>
     <script>