Show notifications when a user's profile name changes
This commit is contained in:
parent
2f015863ca
commit
d75eee015f
44 changed files with 749 additions and 194 deletions
|
@ -479,6 +479,7 @@
|
|||
typingContact: typingContact ? typingContact.format() : null,
|
||||
lastUpdated: this.get('timestamp'),
|
||||
name: this.get('name'),
|
||||
firstName: this.get('profileName'),
|
||||
profileName: this.getProfileName(),
|
||||
timestamp,
|
||||
inboxPosition,
|
||||
|
@ -1081,9 +1082,43 @@
|
|||
id,
|
||||
})
|
||||
);
|
||||
|
||||
this.trigger('newmessage', model);
|
||||
},
|
||||
|
||||
async addProfileChange(profileChange, conversationId) {
|
||||
const message = {
|
||||
conversationId: this.id,
|
||||
type: 'profile-change',
|
||||
sent_at: Date.now(),
|
||||
received_at: Date.now(),
|
||||
unread: true,
|
||||
changedId: conversationId || this.id,
|
||||
profileChange,
|
||||
};
|
||||
|
||||
const id = await window.Signal.Data.saveMessage(message, {
|
||||
Message: Whisper.Message,
|
||||
});
|
||||
const model = MessageController.register(
|
||||
id,
|
||||
new Whisper.Message({
|
||||
...message,
|
||||
id,
|
||||
})
|
||||
);
|
||||
|
||||
this.trigger('newmessage', model);
|
||||
|
||||
if (this.isPrivate()) {
|
||||
ConversationController.getAllGroupsInvolvingId(this.id).then(groups => {
|
||||
_.forEach(groups, group => {
|
||||
group.addProfileChange(profileChange, this.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
async onReadMessage(message, readAt) {
|
||||
// We mark as read everything older than this message - to clean up old stuff
|
||||
// still marked unread in the database. If the user generally doesn't read in
|
||||
|
@ -2489,8 +2524,28 @@
|
|||
);
|
||||
|
||||
// encode
|
||||
const profileFamilyName = family ? stringFromBytes(family) : null;
|
||||
const profileName = given ? stringFromBytes(given) : null;
|
||||
const profileFamilyName = family ? stringFromBytes(family) : null;
|
||||
|
||||
// check for changes
|
||||
const oldName = this.getProfileName();
|
||||
const newName = Util.combineNames(profileName, profileFamilyName);
|
||||
const hadPreviousName = Boolean(oldName);
|
||||
|
||||
// Note that we compare the combined names to ensure that we don't present the exact
|
||||
// same before/after string, even if someone is moving from just first name to
|
||||
// first/last name in their profile data.
|
||||
const nameChanged = oldName !== newName;
|
||||
|
||||
if (!this.isMe() && hadPreviousName && nameChanged) {
|
||||
const change = {
|
||||
type: 'name',
|
||||
oldName,
|
||||
newName,
|
||||
};
|
||||
|
||||
this.addProfileChange(change);
|
||||
}
|
||||
|
||||
// set
|
||||
this.set({ profileName, profileFamilyName });
|
||||
|
|
|
@ -183,6 +183,11 @@
|
|||
type: 'callHistory',
|
||||
data: this.getPropsForCallHistory(),
|
||||
};
|
||||
} else if (this.isProfileChange()) {
|
||||
return {
|
||||
type: 'profileChange',
|
||||
data: this.getPropsForProfileChange(),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -364,6 +369,9 @@
|
|||
isCallHistory() {
|
||||
return this.get('type') === 'call-history';
|
||||
},
|
||||
isProfileChange() {
|
||||
return this.get('type') === 'profile-change';
|
||||
},
|
||||
|
||||
// Props for each message type
|
||||
getPropsForUnsupportedMessage() {
|
||||
|
@ -508,6 +516,16 @@
|
|||
callHistoryDetails: this.get('callHistoryDetails'),
|
||||
};
|
||||
},
|
||||
getPropsForProfileChange() {
|
||||
const change = this.get('profileChange');
|
||||
const changedId = this.get('changedId');
|
||||
|
||||
return {
|
||||
changedContact: this.findAndFormatContact(changedId),
|
||||
change,
|
||||
};
|
||||
},
|
||||
|
||||
getAttachmentsForMessage() {
|
||||
const sticker = this.get('sticker');
|
||||
if (sticker && sticker.data) {
|
||||
|
@ -856,6 +874,17 @@
|
|||
if (this.isUnsupportedMessage()) {
|
||||
return i18n('message--getDescription--unsupported-message');
|
||||
}
|
||||
if (this.isProfileChange()) {
|
||||
const change = this.get('profileChange');
|
||||
const changedId = this.get('changedId');
|
||||
const changedContact = this.findAndFormatContact(changedId);
|
||||
|
||||
return Signal.Util.getStringForProfileChange(
|
||||
change,
|
||||
changedContact,
|
||||
i18n
|
||||
);
|
||||
}
|
||||
if (this.isTapToView()) {
|
||||
if (this.isErased()) {
|
||||
return i18n('message--getDescription--disappearing-media');
|
||||
|
@ -884,7 +913,9 @@
|
|||
if (groupUpdate.left === 'You') {
|
||||
return i18n('youLeftTheGroup');
|
||||
} else if (groupUpdate.left) {
|
||||
return i18n('leftTheGroup', this.getNameForNumber(groupUpdate.left));
|
||||
return i18n('leftTheGroup', [
|
||||
this.getNameForNumber(groupUpdate.left),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!fromContact) {
|
||||
|
@ -894,7 +925,7 @@
|
|||
if (fromContact.isMe()) {
|
||||
messages.push(i18n('youUpdatedTheGroup'));
|
||||
} else {
|
||||
messages.push(i18n('updatedTheGroup', fromContact.getTitle()));
|
||||
messages.push(i18n('updatedTheGroup', [fromContact.getTitle()]));
|
||||
}
|
||||
|
||||
if (groupUpdate.joined && groupUpdate.joined.length) {
|
||||
|
@ -907,10 +938,11 @@
|
|||
|
||||
if (joinedContacts.length > 1) {
|
||||
messages.push(
|
||||
i18n(
|
||||
'multipleJoinedTheGroup',
|
||||
_.map(joinedWithoutMe, contact => contact.getTitle()).join(', ')
|
||||
)
|
||||
i18n('multipleJoinedTheGroup', [
|
||||
_.map(joinedWithoutMe, contact => contact.getTitle()).join(
|
||||
', '
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
if (joinedWithoutMe.length < joinedContacts.length) {
|
||||
|
@ -925,14 +957,14 @@
|
|||
messages.push(i18n('youJoinedTheGroup'));
|
||||
} else {
|
||||
messages.push(
|
||||
i18n('joinedTheGroup', joinedContacts[0].getTitle())
|
||||
i18n('joinedTheGroup', [joinedContacts[0].getTitle()])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupUpdate.name) {
|
||||
messages.push(i18n('titleIsNow', groupUpdate.name));
|
||||
messages.push(i18n('titleIsNow', [groupUpdate.name]));
|
||||
}
|
||||
if (groupUpdate.avatarUpdated) {
|
||||
messages.push(i18n('updatedGroupAvatar'));
|
||||
|
@ -965,18 +997,16 @@
|
|||
return i18n('disappearingMessagesDisabled');
|
||||
}
|
||||
|
||||
return i18n(
|
||||
'timerSetTo',
|
||||
Whisper.ExpirationTimerOptions.getAbbreviated(expireTimer || 0)
|
||||
);
|
||||
return i18n('timerSetTo', [
|
||||
Whisper.ExpirationTimerOptions.getAbbreviated(expireTimer || 0),
|
||||
]);
|
||||
}
|
||||
if (this.isKeyChange()) {
|
||||
const identifier = this.get('key_changed');
|
||||
const conversation = this.findContact(identifier);
|
||||
return i18n(
|
||||
'safetyNumberChangedGroup',
|
||||
conversation ? conversation.getTitle() : null
|
||||
);
|
||||
return i18n('safetyNumberChangedGroup', [
|
||||
conversation ? conversation.getTitle() : null,
|
||||
]);
|
||||
}
|
||||
const contacts = this.get('contact');
|
||||
if (contacts && contacts.length) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* eslint-env node */
|
||||
/* global log */
|
||||
/* eslint-env node, browser */
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const log = typeof window !== 'undefined' ? window.log : console;
|
||||
|
||||
exports.setup = (locale, messages) => {
|
||||
if (!locale) {
|
||||
|
@ -17,18 +19,57 @@ exports.setup = (locale, messages) => {
|
|||
);
|
||||
return '';
|
||||
}
|
||||
if (Array.isArray(substitutions) && substitutions.length > 1) {
|
||||
throw new Error(
|
||||
'Array syntax is not supported with more than one placeholder'
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof substitutions === 'string' ||
|
||||
typeof substitutions === 'number'
|
||||
) {
|
||||
throw new Error('You must provide either a map or an array');
|
||||
}
|
||||
|
||||
const { message } = entry;
|
||||
if (Array.isArray(substitutions)) {
|
||||
if (!substitutions) {
|
||||
return message;
|
||||
} else if (Array.isArray(substitutions)) {
|
||||
return substitutions.reduce(
|
||||
(result, substitution) => result.replace(/\$.+?\$/, substitution),
|
||||
message
|
||||
);
|
||||
} else if (substitutions) {
|
||||
return message.replace(/\$.+?\$/, substitutions);
|
||||
}
|
||||
|
||||
return message;
|
||||
const FIND_REPLACEMENTS = /\$([^$]+)\$/g;
|
||||
|
||||
let match = FIND_REPLACEMENTS.exec(message);
|
||||
let builder = '';
|
||||
let lastTextIndex = 0;
|
||||
|
||||
while (match) {
|
||||
if (lastTextIndex < match.index) {
|
||||
builder += message.slice(lastTextIndex, match.index);
|
||||
}
|
||||
|
||||
const placeholderName = match[1];
|
||||
const value = substitutions[placeholderName];
|
||||
if (!value) {
|
||||
log.error(
|
||||
`i18n: Value not provided for placeholder ${placeholderName} in key '${key}'`
|
||||
);
|
||||
}
|
||||
builder += value || '';
|
||||
|
||||
lastTextIndex = FIND_REPLACEMENTS.lastIndex;
|
||||
match = FIND_REPLACEMENTS.exec(message);
|
||||
}
|
||||
|
||||
if (lastTextIndex < message.length) {
|
||||
builder += message.slice(lastTextIndex);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
getMessage.getLocale = () => locale;
|
||||
|
|
|
@ -93,18 +93,18 @@
|
|||
iconUrl = last.iconUrl;
|
||||
if (numNotifications === 1) {
|
||||
if (last.reaction) {
|
||||
message = i18n('notificationReaction', [
|
||||
lastMessageTitle,
|
||||
last.reaction.emoji,
|
||||
]);
|
||||
message = i18n('notificationReaction', {
|
||||
sender: lastMessageTitle,
|
||||
emoji: last.reaction.emoji,
|
||||
});
|
||||
} else {
|
||||
message = `${i18n('notificationFrom')} ${lastMessageTitle}`;
|
||||
}
|
||||
} else if (last.reaction) {
|
||||
message = i18n('notificationReactionMostRecent', [
|
||||
lastMessageTitle,
|
||||
last.reaction.emoji,
|
||||
]);
|
||||
message = i18n('notificationReactionMostRecent', {
|
||||
sender: lastMessageTitle,
|
||||
emoji: last.reaction.emoji,
|
||||
});
|
||||
} else {
|
||||
message = `${i18n(
|
||||
'notificationMostRecentFrom'
|
||||
|
@ -117,22 +117,22 @@
|
|||
// eslint-disable-next-line prefer-destructuring
|
||||
title = last.title;
|
||||
if (last.reaction) {
|
||||
message = i18n('notificationReactionMessage', [
|
||||
last.title,
|
||||
last.reaction.emoji,
|
||||
last.message,
|
||||
]);
|
||||
message = i18n('notificationReactionMessage', {
|
||||
sender: last.title,
|
||||
emoji: last.reaction.emoji,
|
||||
message: last.message,
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
message = last.message;
|
||||
}
|
||||
} else if (last.reaction) {
|
||||
title = newMessageCountLabel;
|
||||
message = i18n('notificationReactionMessageMostRecent', [
|
||||
last.title,
|
||||
last.reaction.emoji,
|
||||
last.message,
|
||||
]);
|
||||
message = i18n('notificationReactionMessageMostRecent', {
|
||||
sender: last.title,
|
||||
emoji: last.reaction.emoji,
|
||||
message: last.message,
|
||||
});
|
||||
} else {
|
||||
title = newMessageCountLabel;
|
||||
message = `${i18n('notificationMostRecent')} ${last.message}`;
|
||||
|
|
|
@ -210,7 +210,7 @@
|
|||
template: i18n('oneNonImageAtATimeToast'),
|
||||
});
|
||||
Whisper.CannotMixImageAndNonImageAttachmentsToast = Whisper.ToastView.extend({
|
||||
template: i18n('cannotMixImageAdnNonImageAttachments'),
|
||||
template: i18n('cannotMixImageAndNonImageAttachments'),
|
||||
});
|
||||
Whisper.MaxAttachmentsToast = Whisper.ToastView.extend({
|
||||
template: i18n('maximumAttachments'),
|
||||
|
@ -1655,7 +1655,7 @@
|
|||
if (unverified.length > 1) {
|
||||
message = i18n('multipleNoLongerVerified');
|
||||
} else {
|
||||
message = i18n('noLongerVerified', unverified.at(0).getTitle());
|
||||
message = i18n('noLongerVerified', [unverified.at(0).getTitle()]);
|
||||
}
|
||||
|
||||
// Need to re-add, since unverified set may have changed
|
||||
|
@ -2030,10 +2030,10 @@
|
|||
}
|
||||
|
||||
const dialog = new Whisper.ConfirmationDialogView({
|
||||
message: i18n('identityKeyErrorOnSend', [
|
||||
contact.getTitle(),
|
||||
contact.getTitle(),
|
||||
]),
|
||||
message: i18n('identityKeyErrorOnSend', {
|
||||
name1: contact.getTitle(),
|
||||
name2: contact.getTitle(),
|
||||
}),
|
||||
okText: i18n('sendAnyway'),
|
||||
resolve: async () => {
|
||||
await contact.updateVerified();
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
className: 'app-loading-screen',
|
||||
updateProgress(count) {
|
||||
if (count > 0) {
|
||||
const message = i18n('loadingMessages', count.toString());
|
||||
const message = i18n('loadingMessages', [count.toString()]);
|
||||
this.$('.message').text(message);
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue