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; 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() { isPrivate() {
return this.get('type') === 'private'; return this.get('type') === 'private';
}, },
@ -2537,18 +2522,9 @@
return null; return null;
}, },
getAvatar() { getAvatar() {
const title = this.get('name');
const url = this.getAvatarPath(); const url = this.getAvatarPath();
if (url) { return { url: url || null };
return { url };
} else if (this.isPrivate()) {
const symbol = this.isValid() ? '#' : '!';
return {
content: this.getInitials(title) || symbol,
};
}
return { url: null };
}, },
getNotificationIcon() { getNotificationIcon() {

@ -129,12 +129,6 @@ describe('Conversation', () => {
assert.equal(convo.getNumber(), ''); 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', () => { describe('when set to private', () => {
it('correctly validates hex numbers', () => { it('correctly validates hex numbers', () => {
const regularId = new Whisper.Conversation({ const regularId = new Whisper.Conversation({

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

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

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

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

@ -1109,22 +1109,6 @@ export class Message extends React.PureComponent<Props, State> {
`module-message--${direction}`, `module-message--${direction}`,
expiring ? 'module-message--expired' : null 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)} {this.renderError(isIncoming)}
{isRss || isKickedFromGroup {isRss || isKickedFromGroup
@ -1138,6 +1122,22 @@ export class Message extends React.PureComponent<Props, State> {
style={{ style={{
width: isShowingImage ? width : undefined, 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.renderAuthor()}
{this.renderQuote()} {this.renderQuote()}

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

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

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

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

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

@ -32,7 +32,12 @@ export abstract class ClosedGroupMessage extends DataMessage {
} }
protected groupContext(): SignalService.GroupContext { 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 }); return new SignalService.GroupContext({ id });
} }

@ -1,17 +1,29 @@
export class PubKey { export class PubKey {
public static readonly PUBKEY_LEN = 66; 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 // This is a temporary fix to allow groupPubkeys created from mobile to be handled correctly
// They have a different regex to match // 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) // 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; // tslint:disable: member-ordering
public static readonly regexForPubkeys = `((05)?[0-9a-fA-F]{${PubKey.PUBKEY_LEN - public static readonly regexForPubkeys = `((05)?${PubKey.HEX}{64})`;
2}})`; public static readonly PREFIX_GROUP_TEXTSECURE = '__textsecure_group__!';
private static readonly regexForMobileGroupID = `__textsecure_group__![0-9a-fA-F]{${PubKey.MOBILE_GROUP_PUBKEY_LEN}}`;
// prettier-ignore // prettier-ignore
private static readonly regex: RegExp = new RegExp( 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; public readonly key: string;
/** /**

@ -31,7 +31,12 @@ describe('ClosedGroupChatMessage', () => {
.to.have.property('group') .to.have.property('group')
.to.have.deep.property( .to.have.deep.property(
'id', 'id',
new Uint8Array(StringUtils.encode(groupId.key, 'utf8')) new Uint8Array(
StringUtils.encode(
PubKey.PREFIX_GROUP_TEXTSECURE + groupId.key,
'utf8'
)
)
); );
expect(decoded.dataMessage) expect(decoded.dataMessage)
.to.have.property('group') .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 { export function getInitials(name?: string): string | undefined {
if (!name) { if (!name || !name.length) {
return; return;
} }
const cleaned = removeNonInitials(name); if (name.length > 2 && name.startsWith('05')) {
const parts = cleaned.split(' '); return name[2];
const initials = parts.map(part => part.trim()[0]);
if (!initials.length) {
return;
} }
return initials[0]; return name[0];
} }

Loading…
Cancel
Save