Merge pull request #1344 from Bilb/fix-avatar-closed

pull/1345/head
Audric Ackermann 5 years ago committed by GitHub
commit cd44143eaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2504,21 +2504,6 @@
return this.id;
},
getInitials(name) {
if (!name) {
return null;
}
const cleaned = name.replace(/[^A-Za-z\s]+/g, '').replace(/\s+/g, ' ');
const parts = cleaned.split(' ');
const initials = parts.map(part => part.trim()[0]);
if (!initials.length) {
return null;
}
return initials.slice(0, 2).join('');
},
isPrivate() {
return this.get('type') === 'private';
},
@ -2537,18 +2522,9 @@
return null;
},
getAvatar() {
const title = this.get('name');
const url = this.getAvatarPath();
if (url) {
return { url };
} else if (this.isPrivate()) {
const symbol = this.isValid() ? '#' : '!';
return {
content: this.getInitials(title) || symbol,
};
}
return { url: null };
return { url: url || null };
},
getNotificationIcon() {

@ -129,12 +129,6 @@ describe('Conversation', () => {
assert.equal(convo.getNumber(), '');
});
it('has an avatar', () => {
const convo = new Whisper.ConversationCollection().add(attributes);
const avatar = convo.getAvatar();
assert.property(avatar, 'content');
});
describe('when set to private', () => {
it('correctly validates hex numbers', () => {
const regularId = new Whisper.Conversation({

@ -91,9 +91,10 @@ export class Avatar extends React.PureComponent<Props, State> {
}
public render() {
const { avatarPath, size } = this.props;
const { avatarPath, size, memberAvatars } = this.props;
const { imageBroken } = this.state;
const hasImage = avatarPath && !imageBroken;
const isClosedGroupAvatar = memberAvatars && memberAvatars.length;
const hasImage = avatarPath && !imageBroken && !isClosedGroupAvatar;
if (
size !== 28 &&

@ -46,8 +46,10 @@ export class AvatarPlaceHolder extends React.PureComponent<Props, State> {
public render() {
const { borderColor, colors, diameter, name } = this.props;
const diameterWithoutBorder = diameter - 2;
const viewBox = `0 0 ${diameter} ${diameter}`;
const r = diameter / 2;
const rWithoutBorder = diameterWithoutBorder / 2;
if (!this.state.sha512Seed) {
// return grey circle
@ -57,7 +59,7 @@ export class AvatarPlaceHolder extends React.PureComponent<Props, State> {
<circle
cx={r}
cy={r}
r={r}
r={rWithoutBorder}
fill="#d2d2d3"
shape-rendering="geometricPrecision"
stroke={borderColor}
@ -84,7 +86,7 @@ export class AvatarPlaceHolder extends React.PureComponent<Props, State> {
<circle
cx={r}
cy={r}
r={r}
r={rWithoutBorder}
fill={bgColor}
shape-rendering="geometricPrecision"
stroke={borderColor}

@ -312,7 +312,7 @@ class ConversationListItem extends React.PureComponent<Props> {
const triggerId = `conversation-item-${phoneNumber}-ctxmenu`;
return (
<div>
<div key={triggerId}>
<ContextMenuTrigger id={triggerId}>
<div
role="button"

@ -101,7 +101,7 @@ export class MessageSearchResult extends React.PureComponent<Props> {
public renderAvatar() {
const { from } = this.props;
const userName = from.phoneNumber || from.profileName;
const userName = from.profileName || from.phoneNumber;
return (
<Avatar

@ -1109,22 +1109,6 @@ export class Message extends React.PureComponent<Props, State> {
`module-message--${direction}`,
expiring ? 'module-message--expired' : null
)}
role="button"
onClick={event => {
const selection = window.getSelection();
// Text is being selected
if (selection && selection.type === 'Range') {
return;
}
// User clicked on message body
const target = event.target as HTMLDivElement;
if (target.className === 'text-selectable') {
return;
}
this.props.onSelectMessage();
}}
>
{this.renderError(isIncoming)}
{isRss || isKickedFromGroup
@ -1138,6 +1122,22 @@ export class Message extends React.PureComponent<Props, State> {
style={{
width: isShowingImage ? width : undefined,
}}
role="button"
onClick={event => {
const selection = window.getSelection();
// Text is being selected
if (selection && selection.type === 'Range') {
return;
}
// User clicked on message body
const target = event.target as HTMLDivElement;
if (target.className === 'text-selectable') {
return;
}
this.props.onSelectMessage();
}}
>
{this.renderAuthor()}
{this.renderQuote()}

@ -27,7 +27,7 @@ export function usingClosedConversationDetails(WrappedComponent: any) {
void this.fetchClosedConversationDetails();
}
public componentDidUpdate() {
public componentWillReceiveProps() {
void this.fetchClosedConversationDetails();
}
@ -66,7 +66,7 @@ export function usingClosedConversationDetails(WrappedComponent: any) {
members.push(ourPrimary);
}
// no need to forward more than 2 conversations for rendering the group avatar
members.slice(0, 2);
members = members.slice(0, 2);
const memberConvos = await Promise.all(
members.map(async m =>
window.ConversationController.getOrCreateAndWait(m.key, 'private')
@ -79,10 +79,9 @@ export function usingClosedConversationDetails(WrappedComponent: any) {
name: m.get('name') || m.get('profileName') || m.id,
};
});
if (!_.isEqual(memberAvatars, this.state.memberAvatars)) {
this.setState({ memberAvatars });
}
this.setState({ memberAvatars });
} else {
this.setState({ memberAvatars: undefined });
}
}
};

@ -21,15 +21,19 @@ import { StringUtils } from '../session/utils';
import { UserUtil } from '../util';
export async function handleContentMessage(envelope: EnvelopePlus) {
const plaintext = await decrypt(envelope, envelope.content);
try {
const plaintext = await decrypt(envelope, envelope.content);
if (!plaintext) {
window.log.warn('handleContentMessage: plaintext was falsey');
return;
} else if (plaintext instanceof ArrayBuffer && plaintext.byteLength === 0) {
return;
if (!plaintext) {
window.log.warn('handleContentMessage: plaintext was falsey');
return;
} else if (plaintext instanceof ArrayBuffer && plaintext.byteLength === 0) {
return;
}
await innerHandleContentMessage(envelope, plaintext);
} catch (e) {
window.log.warn(e);
}
await innerHandleContentMessage(envelope, plaintext);
}
async function decryptForMediumGroup(
@ -76,7 +80,7 @@ async function decryptForMediumGroup(
window.console.info(
'Dropping message from ourself after decryptForMediumGroup'
);
return;
return null;
}
const plaintext = await decryptWithSenderKey(
@ -86,7 +90,7 @@ async function decryptForMediumGroup(
sourceAsStr
);
return plaintext;
return unpad(plaintext);
}
function unpad(paddedData: ArrayBuffer): ArrayBuffer {
@ -388,81 +392,83 @@ export async function innerHandleContentMessage(
plaintext: ArrayBuffer
): Promise<void> {
const { ConversationController } = window;
const content = SignalService.Content.decode(new Uint8Array(plaintext));
const blocked = await isBlocked(envelope.source);
if (blocked) {
// We want to allow a blocked user message if that's a control message for a known group and the group is not blocked
if (shouldDropBlockedUserMessage(content)) {
window.log.info('Dropping blocked user message');
return;
} else {
window.log.info('Allowing group-control message only from blocked user');
try {
const content = SignalService.Content.decode(new Uint8Array(plaintext));
const blocked = await isBlocked(envelope.source);
if (blocked) {
// We want to allow a blocked user message if that's a control message for a known group and the group is not blocked
if (shouldDropBlockedUserMessage(content)) {
window.log.info('Dropping blocked user message');
return;
} else {
window.log.info(
'Allowing group-control message only from blocked user'
);
}
}
}
const { FALLBACK_MESSAGE } = SignalService.Envelope.Type;
const { FALLBACK_MESSAGE } = SignalService.Envelope.Type;
await ConversationController.getOrCreateAndWait(envelope.source, 'private');
await ConversationController.getOrCreateAndWait(envelope.source, 'private');
if (content.preKeyBundleMessage) {
await handleSessionRequestMessage(envelope, content.preKeyBundleMessage);
} else if (envelope.type !== FALLBACK_MESSAGE) {
const device = new PubKey(envelope.source);
if (content.preKeyBundleMessage) {
await handleSessionRequestMessage(envelope, content.preKeyBundleMessage);
} else if (envelope.type !== FALLBACK_MESSAGE) {
const device = new PubKey(envelope.source);
await SessionProtocol.onSessionEstablished(device);
await libsession.getMessageQueue().processPending(device);
}
await SessionProtocol.onSessionEstablished(device);
await libsession.getMessageQueue().processPending(device);
}
if (content.pairingAuthorisation) {
await handlePairingAuthorisationMessage(
envelope,
content.pairingAuthorisation,
content.dataMessage
);
return;
}
if (content.pairingAuthorisation) {
await handlePairingAuthorisationMessage(
envelope,
content.pairingAuthorisation,
content.dataMessage
);
return;
}
if (content.syncMessage) {
await handleSyncMessage(envelope, content.syncMessage);
return;
}
if (content.syncMessage) {
await handleSyncMessage(envelope, content.syncMessage);
return;
}
if (content.dataMessage) {
if (
content.dataMessage.profileKey &&
content.dataMessage.profileKey.length === 0
) {
content.dataMessage.profileKey = null;
if (content.dataMessage) {
if (
content.dataMessage.profileKey &&
content.dataMessage.profileKey.length === 0
) {
content.dataMessage.profileKey = null;
}
await handleDataMessage(envelope, content.dataMessage);
return;
}
await handleDataMessage(envelope, content.dataMessage);
return;
}
if (content.nullMessage) {
await handleNullMessage(envelope);
return;
}
if (content.callMessage) {
await handleCallMessage(envelope);
return;
}
if (content.receiptMessage) {
await handleReceiptMessage(envelope, content.receiptMessage);
return;
}
if (content.typingMessage) {
if (
content.typingMessage.groupId &&
content.typingMessage.groupId.length === 0
) {
content.typingMessage.groupId = null;
if (content.nullMessage) {
await handleNullMessage(envelope);
return;
}
await handleTypingMessage(envelope, content.typingMessage);
return;
if (content.callMessage) {
await handleCallMessage(envelope);
return;
}
if (content.receiptMessage) {
await handleReceiptMessage(envelope, content.receiptMessage);
return;
}
if (content.typingMessage) {
if (
content.typingMessage.groupId &&
content.typingMessage.groupId.length === 0
) {
content.typingMessage.groupId = null;
}
await handleTypingMessage(envelope, content.typingMessage);
return;
}
} catch (e) {
window.log.warn(e);
}
return;
}
function onReadReceipt(readAt: any, timestamp: any, reader: any) {

@ -52,7 +52,7 @@ export async function encrypt(
const plainText = padPlainTextBuffer(plainTextBuffer);
if (encryptionType === EncryptionType.MediumGroup) {
return encryptForMediumGroup(device, plainTextBuffer);
return encryptForMediumGroup(device, plainText);
}
const address = new window.libsignal.SignalProtocolAddress(device.key, 1);

@ -108,7 +108,7 @@ export class ChatMessage extends DataMessage {
}
dataMessage.profile = profile;
}
if (this.profileKey) {
if (this.profileKey && this.profileKey.length) {
dataMessage.profileKey = this.profileKey;
}

@ -48,7 +48,7 @@ export class ExpirationTimerUpdateMessage extends DataMessage {
if (this.expireTimer) {
data.expireTimer = this.expireTimer;
}
if (this.profileKey) {
if (this.profileKey && this.profileKey.length) {
data.profileKey = this.profileKey;
}

@ -32,7 +32,12 @@ export abstract class ClosedGroupMessage extends DataMessage {
}
protected groupContext(): SignalService.GroupContext {
const id = new Uint8Array(StringUtils.encode(this.groupId.key, 'utf8'));
let groupIdWithPrefix: string = this.groupId.key;
if (!this.groupId.key.startsWith(PubKey.PREFIX_GROUP_TEXTSECURE)) {
groupIdWithPrefix = PubKey.PREFIX_GROUP_TEXTSECURE + this.groupId.key;
}
const encoded = StringUtils.encode(groupIdWithPrefix, 'utf8');
const id = new Uint8Array(encoded);
return new SignalService.GroupContext({ id });
}

@ -1,17 +1,29 @@
export class PubKey {
public static readonly PUBKEY_LEN = 66;
private static readonly HEX = '[0-9a-fA-F]';
// This is a temporary fix to allow groupPubkeys created from mobile to be handled correctly
// They have a different regex to match
// FIXME move this to a new class which validates group ids and use it in all places where we have group ids (message sending included)
public static readonly MOBILE_GROUP_PUBKEY_LEN = 32;
public static readonly regexForPubkeys = `((05)?[0-9a-fA-F]{${PubKey.PUBKEY_LEN -
2}})`;
private static readonly regexForMobileGroupID = `__textsecure_group__![0-9a-fA-F]{${PubKey.MOBILE_GROUP_PUBKEY_LEN}}`;
// tslint:disable: member-ordering
public static readonly regexForPubkeys = `((05)?${PubKey.HEX}{64})`;
public static readonly PREFIX_GROUP_TEXTSECURE = '__textsecure_group__!';
// prettier-ignore
private static readonly regex: RegExp = new RegExp(
`^${PubKey.regexForPubkeys}|${PubKey.regexForMobileGroupID}$`
`^(${PubKey.PREFIX_GROUP_TEXTSECURE})?(05)?(${PubKey.HEX}{64}|${PubKey.HEX}{32})$`
);
/**
* If you want to update this regex. Be sure that those are matches ;
* __textsecure_group__!05010203040506070809a0b0c0d0e0f0ff010203040506070809a0b0c0d0e0f0ff
* __textsecure_group__!010203040506070809a0b0c0d0e0f0ff010203040506070809a0b0c0d0e0f0ff
* __textsecure_group__!05010203040506070809a0b0c0d0e0f0ff
* __textsecure_group__!010203040506070809a0b0c0d0e0f0ff
* 05010203040506070809a0b0c0d0e0f0ff010203040506070809a0b0c0d0e0f0ff
* 010203040506070809a0b0c0d0e0f0ff010203040506070809a0B0c0d0e0f0FF
* 05010203040506070809a0b0c0d0e0f0ff
* 010203040506070809a0b0c0d0e0f0ff
*/
public readonly key: string;
/**

@ -31,7 +31,12 @@ describe('ClosedGroupChatMessage', () => {
.to.have.property('group')
.to.have.deep.property(
'id',
new Uint8Array(StringUtils.encode(groupId.key, 'utf8'))
new Uint8Array(
StringUtils.encode(
PubKey.PREFIX_GROUP_TEXTSECURE + groupId.key,
'utf8'
)
)
);
expect(decoded.dataMessage)
.to.have.property('group')

@ -1,21 +1,11 @@
const BAD_CHARACTERS = /[^A-Za-z\s]+/g;
const WHITESPACE = /\s+/g;
function removeNonInitials(name: string) {
return name.replace(BAD_CHARACTERS, '').replace(WHITESPACE, ' ');
}
export function getInitials(name?: string): string | undefined {
if (!name) {
if (!name || !name.length) {
return;
}
const cleaned = removeNonInitials(name);
const parts = cleaned.split(' ');
const initials = parts.map(part => part.trim()[0]);
if (!initials.length) {
return;
if (name.length > 2 && name.startsWith('05')) {
return name[2];
}
return initials[0];
return name[0];
}

Loading…
Cancel
Save