Honor preferContactAvatars field on AccountRecord

This commit is contained in:
Scott Nonnenberg 2022-01-25 09:44:45 -08:00 committed by GitHub
parent f5eb17e0d1
commit 68a458ec4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 163 additions and 4 deletions

View file

@ -133,6 +133,7 @@ message AccountRecord {
optional PhoneNumberSharingMode phoneNumberSharingMode = 12; optional PhoneNumberSharingMode phoneNumberSharingMode = 12;
optional bool notDiscoverableByPhoneNumber = 13; optional bool notDiscoverableByPhoneNumber = 13;
repeated PinnedConversation pinnedConversations = 14; repeated PinnedConversation pinnedConversations = 14;
optional bool preferContactAvatars = 15;
optional uint32 universalExpireTimer = 17; optional uint32 universalExpireTimer = 17;
optional bool primarySendsSms = 18; optional bool primarySendsSms = 18;
optional string e164 = 19; optional string e164 = 19;

View file

@ -20,6 +20,7 @@ import { UUID, isValidUuid } from './types/UUID';
import { Address } from './types/Address'; import { Address } from './types/Address';
import { QualifiedAddress } from './types/QualifiedAddress'; import { QualifiedAddress } from './types/QualifiedAddress';
import * as log from './logging/log'; import * as log from './logging/log';
import { sleep } from './util/sleep';
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024; const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
@ -784,6 +785,36 @@ export class ConversationController {
return this._initialPromise; return this._initialPromise;
} }
// A number of things outside conversation.attributes affect conversation re-rendering.
// If it's scoped to a given conversation, it's easy to trigger('change'). There are
// important values in storage and the storage service which change rendering pretty
// radically, so this function is necessary to force regeneration of props.
async forceRerender(): Promise<void> {
let count = 0;
const conversations = this._conversations.models.slice();
log.info(
`forceRerender: Starting to loop through ${conversations.length} conversations`
);
for (let i = 0, max = conversations.length; i < max; i += 1) {
const conversation = conversations[i];
if (conversation.cachedProps) {
conversation.oldCachedProps = conversation.cachedProps;
conversation.cachedProps = null;
conversation.trigger('props-change', conversation, false);
count += 1;
}
if (count % 10 === 0) {
// eslint-disable-next-line no-await-in-loop
await sleep(300);
}
}
log.info(`forceRerender: Updated ${count} conversations`);
}
onConvoOpenStart(conversationId: string): void { onConvoOpenStart(conversationId: string): void {
this._conversationOpenStart.set(conversationId, Date.now()); this._conversationOpenStart.set(conversationId, Date.now());
} }

View file

@ -684,8 +684,10 @@ export async function startApp(): Promise<void> {
} }
if ( if (
window.isBeforeVersion(lastVersion, 'v1.36.0-beta.1') && (window.isBeforeVersion(lastVersion, 'v1.36.0-beta.1') &&
window.isAfterVersion(lastVersion, 'v1.35.0-beta.1') window.isAfterVersion(lastVersion, 'v1.35.0-beta.1')) ||
// 5.30 introduced understanding of new storage service AccountRecord fields
window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')
) { ) {
await window.Signal.Services.eraseAllStorageServiceState(); await window.Signal.Services.eraseAllStorageServiceState();
} }

View file

@ -4896,7 +4896,10 @@ export class ConversationModel extends window.Backbone
} }
private getAvatarPath(): undefined | string { private getAvatarPath(): undefined | string {
const avatar = isMe(this.attributes) const shouldShowProfileAvatar =
isMe(this.attributes) ||
window.storage.get('preferContactAvatars') === false;
const avatar = shouldShowProfileAvatar
? this.get('profileAvatar') || this.get('avatar') ? this.get('profileAvatar') || this.get('avatar')
: this.get('avatar') || this.get('profileAvatar'); : this.get('avatar') || this.get('profileAvatar');
return avatar?.path || undefined; return avatar?.path || undefined;

View file

@ -182,6 +182,11 @@ export async function toAccountRecord(
); );
accountRecord.linkPreviews = Boolean(window.Events.getLinkPreviewSetting()); accountRecord.linkPreviews = Boolean(window.Events.getLinkPreviewSetting());
const preferContactAvatars = window.storage.get('preferContactAvatars');
if (preferContactAvatars !== undefined) {
accountRecord.preferContactAvatars = Boolean(preferContactAvatars);
}
const primarySendsSms = window.storage.get('primarySendsSms'); const primarySendsSms = window.storage.get('primarySendsSms');
if (primarySendsSms !== undefined) { if (primarySendsSms !== undefined) {
accountRecord.primarySendsSms = Boolean(primarySendsSms); accountRecord.primarySendsSms = Boolean(primarySendsSms);
@ -855,6 +860,7 @@ export async function mergeAccountRecord(
readReceipts, readReceipts,
sealedSenderIndicators, sealedSenderIndicators,
typingIndicators, typingIndicators,
preferContactAvatars,
primarySendsSms, primarySendsSms,
universalExpireTimer, universalExpireTimer,
e164: accountE164, e164: accountE164,
@ -878,6 +884,15 @@ export async function mergeAccountRecord(
window.storage.put('linkPreviews', linkPreviews); window.storage.put('linkPreviews', linkPreviews);
} }
if (typeof preferContactAvatars === 'boolean') {
const previous = window.storage.get('preferContactAvatars');
window.storage.put('preferContactAvatars', preferContactAvatars);
if (Boolean(previous) !== Boolean(preferContactAvatars)) {
window.ConversationController.forceRerender();
}
}
if (typeof primarySendsSms === 'boolean') { if (typeof primarySendsSms === 'boolean') {
window.storage.put('primarySendsSms', primarySendsSms); window.storage.put('primarySendsSms', primarySendsSms);
} }

View file

@ -0,0 +1,57 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { areArraysMatchingSets } from '../../util/areArraysMatchingSets';
describe('areArraysMatchingSets', () => {
it('returns true if arrays are both empty', () => {
const left: Array<string> = [];
const right: Array<string> = [];
assert.isTrue(areArraysMatchingSets(left, right));
});
it('returns true if arrays are equal', () => {
const left = [1, 2, 3];
const right = [1, 2, 3];
assert.isTrue(areArraysMatchingSets(left, right));
});
it('returns true if arrays are equal but out of order', () => {
const left = [1, 2, 3];
const right = [3, 1, 2];
assert.isTrue(areArraysMatchingSets(left, right));
});
it('returns true if arrays are equal but one has duplicates', () => {
const left = [1, 2, 3, 1];
const right = [1, 2, 3];
assert.isTrue(areArraysMatchingSets(left, right));
});
it('returns false if first array has missing elements', () => {
const left = [1, 2];
const right = [1, 2, 3];
assert.isFalse(areArraysMatchingSets(left, right));
});
it('returns false if second array has missing elements', () => {
const left = [1, 2, 3];
const right = [1, 2];
assert.isFalse(areArraysMatchingSets(left, right));
});
it('returns false if second array is empty', () => {
const left = [1, 2, 3];
const right: Array<number> = [];
assert.isFalse(areArraysMatchingSets(left, right));
});
});

View file

@ -105,6 +105,7 @@ import {
GroupSyncEvent, GroupSyncEvent,
} from './messageReceiverEvents'; } from './messageReceiverEvents';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { areArraysMatchingSets } from '../util/areArraysMatchingSets';
const GROUPV1_ID_LENGTH = 16; const GROUPV1_ID_LENGTH = 16;
const GROUPV2_ID_LENGTH = 32; const GROUPV2_ID_LENGTH = 32;
@ -2629,19 +2630,32 @@ export default class MessageReceiver
envelope: ProcessedEnvelope, envelope: ProcessedEnvelope,
blocked: Proto.SyncMessage.IBlocked blocked: Proto.SyncMessage.IBlocked
): Promise<void> { ): Promise<void> {
let changed = false;
if (blocked.numbers) { if (blocked.numbers) {
const previous = this.storage.get('blocked', []);
log.info('handleBlocked: Blocking these numbers:', blocked.numbers); log.info('handleBlocked: Blocking these numbers:', blocked.numbers);
await this.storage.put('blocked', blocked.numbers); await this.storage.put('blocked', blocked.numbers);
if (!areArraysMatchingSets(previous, blocked.numbers)) {
changed = true;
}
} }
if (blocked.uuids) { if (blocked.uuids) {
const previous = this.storage.get('blocked-uuids', []);
const uuids = blocked.uuids.map((uuid, index) => { const uuids = blocked.uuids.map((uuid, index) => {
return normalizeUuid(uuid, `handleBlocked.uuids.${index}`); return normalizeUuid(uuid, `handleBlocked.uuids.${index}`);
}); });
log.info('handleBlocked: Blocking these uuids:', uuids); log.info('handleBlocked: Blocking these uuids:', uuids);
await this.storage.put('blocked-uuids', uuids); await this.storage.put('blocked-uuids', uuids);
if (!areArraysMatchingSets(previous, uuids)) {
changed = true;
}
} }
if (blocked.groupIds) { if (blocked.groupIds) {
const previous = this.storage.get('blocked-groups', []);
const groupV1Ids: Array<string> = []; const groupV1Ids: Array<string> = [];
const groupIds: Array<string> = []; const groupIds: Array<string> = [];
@ -2661,10 +2675,21 @@ export default class MessageReceiver
'v1:', 'v1:',
groupV1Ids.map(groupId => `group(${groupId})`) groupV1Ids.map(groupId => `group(${groupId})`)
); );
await this.storage.put('blocked-groups', [...groupIds, ...groupV1Ids]);
const ids = [...groupIds, ...groupV1Ids];
await this.storage.put('blocked-groups', ids);
if (!areArraysMatchingSets(previous, ids)) {
changed = true;
}
} }
this.removeFromCache(envelope); this.removeFromCache(envelope);
if (changed) {
log.info('handleBlocked: Block list changed, forcing re-render.');
window.ConversationController.forceRerender();
}
} }
private isBlocked(number: string): boolean { private isBlocked(number: string): boolean {

View file

@ -94,6 +94,7 @@ export type StorageAccessType = {
phoneNumberSharingMode: PhoneNumberSharingMode; phoneNumberSharingMode: PhoneNumberSharingMode;
phoneNumberDiscoverability: PhoneNumberDiscoverability; phoneNumberDiscoverability: PhoneNumberDiscoverability;
pinnedConversationIds: Array<string>; pinnedConversationIds: Array<string>;
preferContactAvatars: boolean;
primarySendsSms: boolean; primarySendsSms: boolean;
// Unlike `number_id` (which also includes device id) this field is only // Unlike `number_id` (which also includes device id) this field is only
// updated whenever we receive a new storage manifest // updated whenever we receive a new storage manifest

View file

@ -0,0 +1,24 @@
// Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export function areArraysMatchingSets<T>(
left: Array<T>,
right: Array<T>
): boolean {
const leftSet = new Set(left);
const rightSet = new Set(right);
for (const item of leftSet) {
if (!rightSet.has(item)) {
return false;
}
}
for (const item of rightSet) {
if (!leftSet.has(item)) {
return false;
}
}
return true;
}