Kaynağa Gözat

Hide input for blocked contacts (#467)

Fixes #462.
IndianaDschones 6 yıl önce
ebeveyn
işleme
2ad735dcd2

+ 3 - 2
karma.conf.js

@@ -22,7 +22,8 @@ module.exports = function(config) {
             'tests/service/browser.js',
             'tests/service/keystore.js',
             'tests/service/notification.js',
-            'tests/helpers.js',
+            'tests/service/receiver.js',
+            'tests/ts/helpers.ts',
         ],
         customLaunchers: {
             Chromium_ci_gitlab: {
@@ -46,4 +47,4 @@ module.exports = function(config) {
 
     config.set(configuration);
 
-}
+};

+ 62 - 6
src/directives/compose_area.ts

@@ -17,9 +17,10 @@
 
 import * as twemoji from 'twemoji';
 
-import {extractText, isActionTrigger, logAdapter, replaceWhitespace} from '../helpers';
+import {extractText, hasValue, isActionTrigger, logAdapter, replaceWhitespace} from '../helpers';
 import {emojify, shortnameToUnicode} from '../helpers/emoji';
 import {BrowserService} from '../services/browser';
+import {ReceiverService} from '../services/receiver';
 import {StringService} from '../services/string';
 import {TimeoutService} from '../services/timeout';
 import {isElementNode, isTextNode} from '../typeguards';
@@ -31,6 +32,7 @@ export default [
     'BrowserService',
     'StringService',
     'TimeoutService',
+    'ReceiverService',
     '$timeout',
     '$translate',
     '$mdDialog',
@@ -40,6 +42,7 @@ export default [
     function(browserService: BrowserService,
              stringService: StringService,
              timeoutService: TimeoutService,
+             receiverService: ReceiverService,
              $timeout: ng.ITimeoutService,
              $translate: ng.translate.ITranslateService,
              $mdDialog: ng.material.IDialogService,
@@ -64,8 +67,10 @@ export default [
                 // Callback that is called when uploading files
                 onUploading: '=',
                 maxTextLength: '=',
+
+                receiver: '<receiver',
             },
-            link(scope: any, element) {
+            link: function(scope: any, element) {
                 // Logging
                 const logTag = '[Directives.ComposeArea]';
 
@@ -100,6 +105,45 @@ export default [
                     toChar?: number,
                 } = null;
 
+                let chatBlocked = false;
+
+                // Function to update blocking state
+                function setChatBlocked(blocked: boolean) {
+                    chatBlocked = blocked;
+                    $log.debug(logTag, 'Receiver blocked:', blocked);
+                    if (blocked) {
+                        sendTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
+                        emojiTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
+                        fileTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
+                        composeDiv.attr('contenteditable', false);
+                        if (emojiKeyboard.hasClass('active')) {
+                            hideEmojiPicker();
+                        }
+                    } else {
+                        if (composeAreaIsEmpty()) {
+                            sendTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
+                        } else {
+                            sendTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
+                        }
+                        emojiTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
+                        fileTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
+                        composeDiv.attr('contenteditable', true);
+                    }
+                }
+
+                // Initialize blocking state
+                setChatBlocked(receiverService.isBlocked(scope.receiver));
+
+                // Watch `isBlocked` flag for changes
+                scope.$watch(
+                    (_scope) => receiverService.isBlocked(_scope.receiver),
+                    (isBlocked: boolean, wasBlocked: boolean) => {
+                        if (isBlocked !== wasBlocked) {
+                            setChatBlocked(isBlocked);
+                        }
+                    },
+                );
+
                 /**
                  * Stop propagation of click events and hold htmlElement of the emojipicker
                  */
@@ -134,6 +178,7 @@ export default [
 
                 // Typing events
                 let stopTypingTimer: ng.IPromise<void> = null;
+
                 function stopTyping() {
                     // We can only stop typing of the timer is set (meaning
                     // that we started typing earlier)
@@ -146,6 +191,7 @@ export default [
                         scope.stopTyping();
                     }
                 }
+
                 function startTyping() {
                     if (stopTypingTimer === null) {
                         // If the timer wasn't set previously, we just
@@ -314,7 +360,7 @@ export default [
                         const next = (file: File, res: ArrayBuffer | null, error: any) => {
                             buffers.set(file, res);
                             if (buffers.size >= fileCounter) {
-                               resolve(buffers);
+                                resolve(buffers);
                             }
                         };
                         for (let n = 0; n < fileCounter; n++) {
@@ -502,6 +548,10 @@ export default [
                 // Emoji trigger is clicked
                 function onEmojiTrigger(ev: UIEvent): void {
                     ev.stopPropagation();
+                    if (chatBlocked) {
+                        hideEmojiPicker();
+                        return;
+                    }
                     // Toggle visibility of picker
                     if (emojiKeyboard.hasClass('active')) {
                         hideEmojiPicker();
@@ -635,6 +685,9 @@ export default [
                 function onFileTrigger(ev: UIEvent): void {
                     ev.preventDefault();
                     ev.stopPropagation();
+                    if (chatBlocked) {
+                        return;
+                    }
                     const input = element[0].querySelector('.file-input') as HTMLInputElement;
                     input.click();
                 }
@@ -642,6 +695,9 @@ export default [
                 function onSendTrigger(ev: UIEvent): boolean {
                     ev.preventDefault();
                     ev.stopPropagation();
+                    if (chatBlocked) {
+                        return;
+                    }
                     return sendText();
                 }
 
@@ -668,7 +724,7 @@ export default [
                 }
 
                 // return the html code position of the container element
-                function getPositions(offset: number, container: Node): {html: number, text: number} {
+                function getPositions(offset: number, container: Node): { html: number, text: number } {
                     let pos = null;
                     let textPos = null;
 
@@ -684,7 +740,7 @@ export default [
                             pos = 0;
                             textPos = 0;
                         } else {
-                            selectedElement =  container.previousSibling;
+                            selectedElement = container.previousSibling;
                             pos = offset;
                             textPos = offset;
                         }
@@ -817,7 +873,7 @@ export default [
                     if (args.query && args.mention) {
                         // Insert resulting HTML
                         insertMention(args.mention, caretPosition ? caretPosition.to - args.query.length : null,
-                            caretPosition ?  caretPosition.to : null);
+                            caretPosition ? caretPosition.to : null);
                     }
                 }));
 

+ 3 - 2
src/partials/messenger.conversation.html

@@ -1,6 +1,6 @@
 <div id="conversation" class="drag-container">
     <drag-file
-            ng-if="!ctrl.locked"
+            ng-if="!ctrl.locked && !ctrl.receiver.isBlocked"
             submit="ctrl.submit"
             on-uploading="ctrl.onUploading"></drag-file>
 
@@ -123,7 +123,8 @@
                     start-typing="ctrl.startTyping"
                     stop-typing="ctrl.stopTyping"
                     on-uploading="ctrl.onUploading"
-                    max-text-length="ctrl.maxTextLength">
+                    max-text-length="ctrl.maxTextLength"
+                    receiver="ctrl.receiver">
             </compose-area>
         </div>
     </div>

+ 18 - 0
src/services/receiver.ts

@@ -15,6 +15,7 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
+import {hasValue} from '../helpers';
 import {isContactReceiver} from '../typeguards';
 
 export class ReceiverService {
@@ -58,4 +59,21 @@ export class ReceiverService {
             && receiver.id.substr(0, 1) === '*';
 
     }
+
+    /**
+     * Check if a receiver is blocked.
+     * If the receiver isn't a contact or does not have the blocked flag, he is not blocked.
+     * Otherwise the isBlocked flag is evaluated
+     * @param receiver
+     */
+    public isBlocked(receiver: threema.Receiver | null): boolean {
+        if (!hasValue(receiver)) {
+            return false;
+        }
+        if (isContactReceiver(receiver) && hasValue(receiver.isBlocked)) {
+            return receiver.isBlocked;
+        } else {
+            return false;
+        }
+    }
 }

+ 74 - 0
tests/service/receiver.js

@@ -0,0 +1,74 @@
+describe('ReceiverService', function () {
+
+    let $service;
+
+    // Ignoring page reload request
+    beforeAll(() => window.onbeforeunload = () => null);
+
+    beforeEach(function () {
+
+        module('3ema.services');
+
+        // Inject the service
+        inject(function (ReceiverService) {
+            $service = ReceiverService;
+        });
+
+    });
+
+    describe('Receiver', () => {
+        it('is not blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'contact',
+                isBlocked: false
+            })).toEqual(false);
+        });
+        it('is blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'contact',
+                isBlocked: true
+            })).toEqual(true);
+        });
+    });
+
+    describe('Invalid receiver', () => {
+        it('group is not blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'group',
+                isBlocked: false
+            })).toEqual(false);
+        });
+        it('invalidType is not blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'invalidType',
+                isBlocked: true
+            })).toEqual(false);
+        });
+    });
+
+    describe('Receiver without isBlocked flag', () => {
+        it('is not blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'contact'
+            })).toEqual(false);
+        });
+        it('and invalid type is blocked', () => {
+            expect($service.isBlocked({
+                id: 'FOOOOBAR',
+                type: 'invalidType'
+            })).toEqual(false);
+        });
+    });
+
+    describe('Undefined receiver', () => {
+        it('is not blocked', () => {
+            expect($service.isBlocked(undefined)).toEqual(false);
+        });
+    });
+
+});