소스 검색

Fix updating of avatars in contact autocomplete box (#643)

Because the avatar directive was recycled by the autocomplete directive,
and because receiver updates did not work properly in the avatar
directive, sometimes the wrong avatars were displayed in the
autocomplete box that is shown for example when adding new members
to a group.
Danilo Bargen 6 년 전
부모
커밋
242635cbb5
2개의 변경된 파일106개의 추가작업 그리고 17개의 파일을 삭제
  1. 69 8
      src/directives/avatar.ts
  2. 37 9
      src/directives/contact_badge.ts

+ 69 - 8
src/directives/avatar.ts

@@ -15,7 +15,7 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
 
-import {bufferToUrl, logAdapter} from '../helpers';
+import {bufferToUrl, hasValue, logAdapter} from '../helpers';
 import {isEchoContact, isGatewayContact} from '../receiver_helpers';
 import {TimeoutService} from '../services/timeout';
 import {WebClientService} from '../services/webclient';
@@ -37,6 +37,48 @@ export default [
                 receiver: '=eeeReceiver',
                 resolution: '=eeeResolution',
             },
+            link: function(scope, elem, attrs) {
+                scope.$watch(
+                    () => scope.ctrl.receiver,
+                    (newReceiver: threema.Receiver, oldReceiver: threema.Receiver) => {
+                        // Register for receiver changes. When something relevant changes, call the update function.
+                        // This prevents processing the avatar more often than necessary.
+
+                        if (!hasValue(newReceiver)) {
+                            // New receiver has no value
+                            return;
+                        }
+                        if (!hasValue(oldReceiver)) {
+                            // New receiver has value, old receiver doesn't
+                            scope.ctrl.update(false);
+                            return;
+                        }
+
+                        // Check for changes in relevant attributes
+                        if (newReceiver.id !== oldReceiver.id ||
+                            newReceiver.type !== oldReceiver.type ||
+                            newReceiver.color !== oldReceiver.color ||
+                            newReceiver.displayName !== oldReceiver.displayName) {
+                            scope.ctrl.update(false);
+                            return;
+                        }
+
+                        // Check for changes in the avatar itself
+                        if (hasValue(newReceiver.avatar)) {
+                            if (hasValue(oldReceiver.avatar)) {
+                                if (newReceiver.avatar.high !== oldReceiver.avatar.high ||
+                                    newReceiver.avatar.low !== oldReceiver.avatar.low) {
+                                    scope.ctrl.update(false);
+                                    return;
+                                }
+                            } else {
+                                scope.ctrl.update(false);
+                                return;
+                            }
+                        }
+                    },
+                );
+            },
             controllerAs: 'ctrl',
             controller: [function() {
                 this.logTag = '[Directives.Avatar]';
@@ -51,7 +93,7 @@ export default [
                     low: null,
                 };
                 this.avatarToUri = (data: ArrayBuffer, res: 'high' | 'low') => {
-                    if (data === null || data === undefined) {
+                    if (!hasValue(data)) {
                         return '';
                     }
                     if (avatarUri[res] === null) {
@@ -65,16 +107,35 @@ export default [
                     return avatarUri[res];
                 };
 
-                this.$onInit = function() {
+                /**
+                 * Update data when the receiver changes.
+                 */
+                this.update = (initial: boolean) => {
+                    // Reset avatar cache
+                    avatarUri.high = null;
+                    avatarUri.low = null;
+
+                    // Get receiver
+                    const receiver: threema.Receiver = this.receiver;
 
+                    // Set initial values
                     this.highResolution = this.resolution === 'high';
                     this.isLoading = this.highResolution;
-                    this.backgroundColor = (this.receiver as threema.Receiver).color;
-                    this.receiverName = (this.receiver as threema.Receiver).displayName;
-                    this.avatarClass = () => {
-                        return 'avatar-' + this.resolution + (this.isLoading ? ' is-loading' : '');
-                    };
+                    this.backgroundColor = receiver.color;
+                    this.receiverName = receiver.displayName;
+                };
+
+                this.$onInit = function() {
+                    this.update(true);
+
+                    /**
+                     * Return the CSS class for the avatar.
+                     */
+                    this.avatarClass = () => 'avatar-' + this.resolution + (this.isLoading ? ' is-loading' : '');
 
+                    /**
+                     * Return whether or not an avatar is available.
+                     */
                     this.avatarExists = () => {
                         if (this.receiver.avatar === undefined
                             || this.receiver.avatar[this.resolution] === undefined

+ 37 - 9
src/directives/contact_badge.ts

@@ -19,6 +19,7 @@
 
 import {StateService as UiStateService} from '@uirouter/angularjs';
 
+import {hasValue} from '../helpers';
 import {WebClientService} from '../services/webclient';
 
 /**
@@ -37,23 +38,50 @@ export default [
                 linked: '=?eeeLinked',
                 onRemove: '=?eeeOnRemove',
             },
+            link: function(scope, elem, attrs) {
+                // Manual change detection: Identity
+                scope.$watch(
+                    () => scope.ctrl.identity,
+                    (newIdentity, oldIdentity) => {
+                        if (hasValue(newIdentity) && newIdentity !== oldIdentity) {
+                            scope.ctrl.updateReceiverData();
+                        }
+                    },
+                );
+                // Manual change detection: Contact receiver
+                scope.$watch(
+                    () => scope.ctrl.contactReceiver,
+                    (newReceiver, oldReceiver) => {
+                        if (hasValue(newReceiver)) {
+                            if (!hasValue(oldReceiver) || newReceiver.id !== oldReceiver.id) {
+                                scope.ctrl.updateReceiverData();
+                            }
+                        }
+                    },
+                );
+            },
             controllerAs: 'ctrl',
             controller: [function() {
-                this.$onInit = function() {
+                this.click = () => {
+                    if (this.linked !== undefined
+                        && this.linked === true) {
+                        $state.go('messenger.home.conversation', {type: 'contact', id: this.identity, initParams: null});
+                    }
+                };
+
+                this.showActions = this.onRemove !== undefined;
+
+                this.updateReceiverData = () => {
+                    // Either a receiver or an identity is set
                     if (this.contactReceiver === undefined) {
                         this.contactReceiver = webClientService.contacts.get(this.identity);
                     } else {
                         this.identity = this.contactReceiver.id;
                     }
+                };
 
-                    this.click = () => {
-                        if (this.linked !== undefined
-                            && this.linked === true) {
-                            $state.go('messenger.home.conversation', {type: 'contact', id: this.identity, initParams: null});
-                        }
-                    };
-
-                    this.showActions = this.onRemove !== undefined;
+                this.$onInit = function() {
+                    this.updateReceiverData();
                 };
             }],
             template: `