Note to Self

pull/272/head
Scott Nonnenberg 6 years ago
parent 681ca363fe
commit a43a78731a

@ -1544,6 +1544,10 @@
"message": "Dark", "message": "Dark",
"description": "Label text for dark theme" "description": "Label text for dark theme"
}, },
"noteToSelf": {
"message": "Note to Self",
"description": "Name for the conversation with your own phone number"
},
"hideMenuBar": { "hideMenuBar": {
"message": "Hide menu bar", "message": "Hide menu bar",
"description": "Label text for menu bar visibility setting" "description": "Label text for menu bar visibility setting"

@ -0,0 +1 @@
<svg id="Export" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28"><title>note-28</title><path d="M21,3H7A2,2,0,0,0,5,5V23a2,2,0,0,0,2,2H21a2,2,0,0,0,2-2V5A2,2,0,0,0,21,3ZM17,19.5H7V18H17ZM21,16H7V14.5H21Zm0-3.5H7V11H21ZM21,9H7V7.5H21Z"/></svg>

After

Width:  |  Height:  |  Size: 249 B

@ -312,6 +312,7 @@
const result = { const result = {
...this.format(), ...this.format(),
isMe: this.isMe(),
conversationType: this.isPrivate() ? 'direct' : 'group', conversationType: this.isPrivate() ? 'direct' : 'group',
lastUpdated: this.get('timestamp'), lastUpdated: this.get('timestamp'),
@ -908,6 +909,25 @@
return null; return null;
} }
const attachmentsWithData = await Promise.all(
messageWithSchema.attachments.map(loadAttachmentData)
);
// Special-case the self-send case - we send only a sync message
if (this.isMe()) {
const dataMessage = await textsecure.messaging.getMessageProto(
destination,
body,
attachmentsWithData,
quote,
preview,
now,
expireTimer,
profileKey
);
return message.sendSyncMessageOnly(dataMessage);
}
const conversationType = this.get('type'); const conversationType = this.get('type');
const sendFunction = (() => { const sendFunction = (() => {
switch (conversationType) { switch (conversationType) {
@ -922,10 +942,6 @@
} }
})(); })();
const attachmentsWithData = await Promise.all(
messageWithSchema.attachments.map(loadAttachmentData)
);
const options = this.getSendOptions(); const options = this.getSendOptions();
return message.send( return message.send(
this.wrapSend( this.wrapSend(

@ -736,10 +736,25 @@
const quoteWithData = await loadQuoteData(this.get('quote')); const quoteWithData = await loadQuoteData(this.get('quote'));
const previewWithData = await loadPreviewData(this.get('preview')); const previewWithData = await loadPreviewData(this.get('preview'));
const conversation = this.getConversation(); // Special-case the self-send case - we send only a sync message
const options = conversation.getSendOptions(); if (numbers.length === 1 && numbers[0] === this.OUR_NUMBER) {
const [number] = numbers;
const dataMessage = await textsecure.messaging.getMessageProto(
number,
this.get('body'),
attachmentsWithData,
quoteWithData,
previewWithData,
this.get('sent_at'),
this.get('expireTimer'),
profileKey
);
return this.sendSyncMessageOnly(dataMessage);
}
let promise; let promise;
const conversation = this.getConversation();
const options = conversation.getSendOptions();
if (conversation.isPrivate()) { if (conversation.isPrivate()) {
const [number] = numbers; const [number] = numbers;
@ -794,7 +809,11 @@
// One caller today: ConversationView.forceSend() // One caller today: ConversationView.forceSend()
async resend(number) { async resend(number) {
const error = this.removeOutgoingErrors(number); const error = this.removeOutgoingErrors(number);
if (error) { if (!error) {
window.log.warn('resend: requested number was not present in errors');
return null;
}
const profileKey = null; const profileKey = null;
const attachmentsWithData = await Promise.all( const attachmentsWithData = await Promise.all(
(this.get('attachments') || []).map(loadAttachmentData) (this.get('attachments') || []).map(loadAttachmentData)
@ -802,6 +821,21 @@
const quoteWithData = await loadQuoteData(this.get('quote')); const quoteWithData = await loadQuoteData(this.get('quote'));
const previewWithData = await loadPreviewData(this.get('preview')); const previewWithData = await loadPreviewData(this.get('preview'));
// Special-case the self-send case - we send only a sync message
if (number === this.OUR_NUMBER) {
const dataMessage = await textsecure.messaging.getMessageProto(
number,
this.get('body'),
attachmentsWithData,
quoteWithData,
previewWithData,
this.get('sent_at'),
this.get('expireTimer'),
profileKey
);
return this.sendSyncMessageOnly(dataMessage);
}
const { wrap, sendOptions } = ConversationController.prepareForSend( const { wrap, sendOptions } = ConversationController.prepareForSend(
number number
); );
@ -817,8 +851,7 @@
sendOptions sendOptions
); );
this.send(wrap(promise)); return this.send(wrap(promise));
}
}, },
removeOutgoingErrors(number) { removeOutgoingErrors(number) {
const errors = _.partition( const errors = _.partition(
@ -912,7 +945,7 @@
this.trigger('done'); this.trigger('done');
// This is used by sendSyncMessage, then set to null // This is used by sendSyncMessage, then set to null
if (result.dataMessage) { if (!this.get('synced') && result.dataMessage) {
this.set({ dataMessage: result.dataMessage }); this.set({ dataMessage: result.dataMessage });
} }
@ -1013,6 +1046,35 @@
return false; return false;
}, },
async sendSyncMessageOnly(dataMessage) {
this.set({ dataMessage });
try {
await this.sendSyncMessage();
this.set({
delivered_to: [this.OUR_NUMBER],
read_by: [this.OUR_NUMBER],
});
} catch (result) {
const errors = (result && result.errors) || [
new Error('Unknown error'),
];
this.set({ errors });
} finally {
await window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message,
});
this.trigger('done');
const errors = this.get('errors');
if (errors) {
this.trigger('send-error', errors);
} else {
this.trigger('sent');
}
}
},
sendSyncMessage() { sendSyncMessage() {
const ourNumber = textsecure.storage.user.getNumber(); const ourNumber = textsecure.storage.user.getNumber();
const { wrap, sendOptions } = ConversationController.prepareForSend( const { wrap, sendOptions } = ConversationController.prepareForSend(
@ -1021,7 +1083,7 @@
); );
this.syncPromise = this.syncPromise || Promise.resolve(); this.syncPromise = this.syncPromise || Promise.resolve();
this.syncPromise = this.syncPromise.then(() => { const next = () => {
const dataMessage = this.get('dataMessage'); const dataMessage = this.get('dataMessage');
if (this.get('synced') || !dataMessage) { if (this.get('synced') || !dataMessage) {
return Promise.resolve(); return Promise.resolve();
@ -1036,16 +1098,20 @@
this.get('unidentifiedDeliveries'), this.get('unidentifiedDeliveries'),
sendOptions sendOptions
) )
).then(() => { ).then(result => {
this.set({ this.set({
synced: true, synced: true,
dataMessage: null, dataMessage: null,
}); });
return window.Signal.Data.saveMessage(this.attributes, { return window.Signal.Data.saveMessage(this.attributes, {
Message: Whisper.Message, Message: Whisper.Message,
}).then(() => result);
}); });
}); };
});
this.syncPromise = this.syncPromise.then(next, next);
return this.syncPromise;
}, },
async saveErrors(providedErrors) { async saveErrors(providedErrors) {
@ -1312,6 +1378,14 @@
}); });
} }
// A sync'd message to ourself is automatically considered read and delivered
if (conversation.isMe()) {
message.set({
read_by: conversation.getRecipients(),
delivered_to: conversation.getRecipients(),
});
}
message.set({ recipients: conversation.getRecipients() }); message.set({ recipients: conversation.getRecipients() });
} }

@ -1,6 +1,4 @@
/* global ConversationController: false */ /* global ConversationController, i18n, textsecure, Whisper */
/* global i18n: false */
/* global Whisper: false */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(function() { (function() {
@ -81,9 +79,19 @@
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
this.pending = this.pending.then(() => this.pending = this.pending.then(() =>
this.typeahead.search(query).then(() => { this.typeahead.search(query).then(() => {
this.typeahead_view.collection.reset( let results = this.typeahead.filter(isSearchable);
this.typeahead.filter(isSearchable) const noteToSelf = i18n('noteToSelf');
); if (noteToSelf.toLowerCase().indexOf(query.toLowerCase()) !== -1) {
const ourNumber = textsecure.storage.user.getNumber();
const conversation = ConversationController.get(ourNumber);
if (conversation) {
// ensure that we don't have duplicates in our results
results = results.filter(item => item.id !== ourNumber);
results.unshift(conversation);
}
}
this.typeahead_view.collection.reset(results);
}) })
); );
/* eslint-enable more/no-then */ /* eslint-enable more/no-then */

@ -759,6 +759,37 @@ MessageSender.prototype = {
}); });
}, },
async getMessageProto(
number,
body,
attachments,
quote,
preview,
timestamp,
expireTimer,
profileKey
) {
const attributes = {
recipients: [number],
body,
timestamp,
attachments,
quote,
preview,
expireTimer,
profileKey,
};
const message = new Message(attributes);
await Promise.all([
this.uploadAttachments(message),
this.uploadThumbnails(message),
this.uploadLinkPreviews(message),
]);
return message.toArrayBuffer();
},
sendMessageToNumber( sendMessageToNumber(
number, number,
messageText, messageText,
@ -1110,6 +1141,7 @@ textsecure.MessageSender = function MessageSenderWrapper(
this.sendReadReceipts = sender.sendReadReceipts.bind(sender); this.sendReadReceipts = sender.sendReadReceipts.bind(sender);
this.makeProxiedRequest = sender.makeProxiedRequest.bind(sender); this.makeProxiedRequest = sender.makeProxiedRequest.bind(sender);
this.getProxiedSize = sender.getProxiedSize.bind(sender); this.getProxiedSize = sender.getProxiedSize.bind(sender);
this.getMessageProto = sender.getMessageProto.bind(sender);
}; };
textsecure.MessageSender.prototype = { textsecure.MessageSender.prototype = {

@ -1348,7 +1348,7 @@
} }
.module-conversation-header__title { .module-conversation-header__title {
margin-left: 8px; margin-left: 6px;
min-width: 0; min-width: 0;
font-size: 16px; font-size: 16px;
@ -1356,8 +1356,8 @@
font-weight: 300; font-weight: 300;
color: $color-gray-90; color: $color-gray-90;
// width of avatar (28px) and our 8px left margin // width of avatar (28px) and our 6px left margin
max-width: calc(100% - 36px); max-width: calc(100% - 34px);
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -2036,6 +2036,12 @@
width: 42px; width: 42px;
} }
.module-avatar__icon--note-to-self {
width: 70%;
height: 70%;
@include color-svg('../images/note-28.svg', $color-white);
}
.module-avatar--no-image { .module-avatar--no-image {
background-color: $color-conversation-grey; background-color: $color-conversation-grey;
} }

@ -1098,6 +1098,10 @@ body.dark-theme {
color: $color-dark-05; color: $color-dark-05;
} }
.module-conversation-header__note-to-self {
color: $color-dark-05;
}
.module-conversation-header__title__verified-icon { .module-conversation-header__title__verified-icon {
@include color-svg('../images/verified-check.svg', $color-dark-05); @include color-svg('../images/verified-check.svg', $color-dark-05);
} }
@ -1262,11 +1266,15 @@ body.dark-theme {
} }
.module-avatar__icon--group { .module-avatar__icon--group {
@include color-svg('../images/profile-group.svg', $color-gray-05); background-color: $color-gray-05;
} }
.module-avatar__icon--direct { .module-avatar__icon--direct {
@include color-svg('../images/profile-individual.svg', $color-gray-05); background-color: $color-gray-05;
}
.module-avatar__icon--note-to-self {
background-color: $color-gray-05;
} }
.module-avatar--no-image { .module-avatar--no-image {

@ -63,6 +63,45 @@
</util.ConversationContext> </util.ConversationContext>
``` ```
### Note to self
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={80}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={48}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={36}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### All colors ### All colors
```jsx ```jsx

@ -9,6 +9,7 @@ interface Props {
color?: string; color?: string;
conversationType: 'group' | 'direct'; conversationType: 'group' | 'direct';
i18n: Localizer; i18n: Localizer;
noteToSelf?: boolean;
name?: string; name?: string;
phoneNumber?: string; phoneNumber?: string;
profileName?: string; profileName?: string;
@ -63,11 +64,23 @@ export class Avatar extends React.Component<Props, State> {
} }
public renderNoImage() { public renderNoImage() {
const { conversationType, name, size } = this.props; const { conversationType, name, noteToSelf, size } = this.props;
const initials = getInitials(name); const initials = getInitials(name);
const isGroup = conversationType === 'group'; const isGroup = conversationType === 'group';
if (noteToSelf) {
return (
<div
className={classNames(
'module-avatar__icon',
'module-avatar__icon--note-to-self',
`module-avatar__icon--${size}`
)}
/>
);
}
if (!isGroup && initials) { if (!isGroup && initials) {
return ( return (
<div <div
@ -93,10 +106,10 @@ export class Avatar extends React.Component<Props, State> {
} }
public render() { public render() {
const { avatarPath, color, size } = this.props; const { avatarPath, color, size, noteToSelf } = this.props;
const { imageBroken } = this.state; const { imageBroken } = this.state;
const hasImage = avatarPath && !imageBroken; const hasImage = !noteToSelf && avatarPath && !imageBroken;
if (size !== 28 && size !== 36 && size !== 48 && size !== 80) { if (size !== 28 && size !== 36 && size !== 48 && size !== 80) {
throw new Error(`Size ${size} is not supported!`); throw new Error(`Size ${size} is not supported!`);

@ -38,6 +38,27 @@
</util.LeftPaneContext> </util.LeftPaneContext>
``` ```
#### Conversation with yourself
```jsx
<util.LeftPaneContext theme={util.theme}>
<ConversationListItem
isMe={true}
phoneNumber="(202) 555-0011"
conversationType={'direct'}
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Just a second',
status: 'read',
}}
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
</util.LeftPaneContext>
```
#### All types of status #### All types of status
```jsx ```jsx

@ -16,6 +16,7 @@ interface Props {
color?: string; color?: string;
conversationType: 'group' | 'direct'; conversationType: 'group' | 'direct';
avatarPath?: string; avatarPath?: string;
isMe: boolean;
lastUpdated: number; lastUpdated: number;
unreadCount: number; unreadCount: number;
@ -38,6 +39,7 @@ export class ConversationListItem extends React.Component<Props> {
color, color,
conversationType, conversationType,
i18n, i18n,
isMe,
name, name,
phoneNumber, phoneNumber,
profileName, profileName,
@ -48,6 +50,7 @@ export class ConversationListItem extends React.Component<Props> {
<Avatar <Avatar
avatarPath={avatarPath} avatarPath={avatarPath}
color={color} color={color}
noteToSelf={isMe}
conversationType={conversationType} conversationType={conversationType}
i18n={i18n} i18n={i18n}
name={name} name={name}
@ -78,6 +81,7 @@ export class ConversationListItem extends React.Component<Props> {
const { const {
unreadCount, unreadCount,
i18n, i18n,
isMe,
lastUpdated, lastUpdated,
name, name,
phoneNumber, phoneNumber,
@ -94,12 +98,16 @@ export class ConversationListItem extends React.Component<Props> {
: null : null
)} )}
> >
{isMe ? (
i18n('noteToSelf')
) : (
<ContactName <ContactName
phoneNumber={phoneNumber} phoneNumber={phoneNumber}
name={name} name={name}
profileName={profileName} profileName={profileName}
i18n={i18n} i18n={i18n}
/> />
)}
</div> </div>
<div <div
className={classNames( className={classNames(

@ -5,7 +5,8 @@ Note the five items in gear menu, and the second-level menu with disappearing me
#### With name and profile, verified #### With name and profile, verified
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
i18n={util.i18n} i18n={util.i18n}
color="red" color="red"
isVerified={true} isVerified={true}
@ -23,57 +24,67 @@ Note the five items in gear menu, and the second-level menu with disappearing me
onShowAllMedia={() => console.log('onShowAllMedia')} onShowAllMedia={() => console.log('onShowAllMedia')}
onShowGroupMembers={() => console.log('onShowGroupMembers')} onShowGroupMembers={() => console.log('onShowGroupMembers')}
onGoBack={() => console.log('onGoBack')} onGoBack={() => console.log('onGoBack')}
/> />
</util.ConversationContext>
``` ```
#### With name, not verified, no avatar #### With name, not verified, no avatar
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
i18n={util.i18n} i18n={util.i18n}
color="blue" color="blue"
isVerified={false} isVerified={false}
name="Someone 🔥 Somewhere" name="Someone 🔥 Somewhere"
phoneNumber="(202) 555-0002" phoneNumber="(202) 555-0002"
id="2" id="2"
/> />
</util.ConversationContext>
``` ```
#### Profile, no name #### Profile, no name
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
i18n={util.i18n} i18n={util.i18n}
color="teal" color="teal"
isVerified={false} isVerified={false}
phoneNumber="(202) 555-0003" phoneNumber="(202) 555-0003"
id="3" id="3"
profileName="🔥Flames🔥" profileName="🔥Flames🔥"
/> />
</util.ConversationContext>
``` ```
#### No name, no profile, no color #### No name, no profile, no color
```jsx ```jsx
<ConversationHeader i18n={util.i18n} phoneNumber="(202) 555-0011" id="11" /> <util.ConversationContext theme={util.theme}>
<ConversationHeader i18n={util.i18n} phoneNumber="(202) 555-0011" id="11" />
</util.ConversationContext>
``` ```
### With back button ### With back button
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
showBackButton={true} showBackButton={true}
color="deep_orange" color="deep_orange"
i18n={util.i18n} i18n={util.i18n}
phoneNumber="(202) 555-0004" phoneNumber="(202) 555-0004"
id="4" id="4"
/> />
</util.ConversationContext>
``` ```
### Disappearing messages set ### Disappearing messages set
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
color="indigo" color="indigo"
i18n={util.i18n} i18n={util.i18n}
phoneNumber="(202) 555-0005" phoneNumber="(202) 555-0005"
@ -98,7 +109,8 @@ Note the five items in gear menu, and the second-level menu with disappearing me
onShowAllMedia={() => console.log('onShowAllMedia')} onShowAllMedia={() => console.log('onShowAllMedia')}
onShowGroupMembers={() => console.log('onShowGroupMembers')} onShowGroupMembers={() => console.log('onShowGroupMembers')}
onGoBack={() => console.log('onGoBack')} onGoBack={() => console.log('onGoBack')}
/> />
</util.ConversationContext>
``` ```
### In a group ### In a group
@ -106,7 +118,8 @@ Note the five items in gear menu, and the second-level menu with disappearing me
Note that the menu should includes 'Show Members' instead of 'Show Safety Number' Note that the menu should includes 'Show Members' instead of 'Show Safety Number'
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
i18n={util.i18n} i18n={util.i18n}
color="green" color="green"
phoneNumber="(202) 555-0006" phoneNumber="(202) 555-0006"
@ -121,19 +134,22 @@ Note that the menu should includes 'Show Members' instead of 'Show Safety Number
onShowAllMedia={() => console.log('onShowAllMedia')} onShowAllMedia={() => console.log('onShowAllMedia')}
onShowGroupMembers={() => console.log('onShowGroupMembers')} onShowGroupMembers={() => console.log('onShowGroupMembers')}
onGoBack={() => console.log('onGoBack')} onGoBack={() => console.log('onGoBack')}
/> />
</util.ConversationContext>
``` ```
### In chat with yourself ### In chat with yourself
Note that the menu should not have a 'Show Safety Number' entry. This is the 'Note to self' conversation. Note that the menu should not have a 'Show Safety Number' entry.
```jsx ```jsx
<ConversationHeader <util.ConversationContext theme={util.theme}>
<ConversationHeader
color="cyan" color="cyan"
i18n={util.i18n} i18n={util.i18n}
phoneNumber="(202) 555-0007" phoneNumber="(202) 555-0007"
id="7" id="7"
isMe={true} isMe={true}
/> />
</util.ConversationContext>
``` ```

@ -84,7 +84,22 @@ export class ConversationHeader extends React.Component<Props> {
} }
public renderTitle() { public renderTitle() {
const { name, phoneNumber, i18n, profileName, isVerified } = this.props; const {
name,
phoneNumber,
i18n,
isMe,
profileName,
isVerified,
} = this.props;
if (isMe) {
return (
<div className="module-conversation-header__title">
{i18n('noteToSelf')}
</div>
);
}
return ( return (
<div className="module-conversation-header__title"> <div className="module-conversation-header__title">
@ -113,6 +128,7 @@ export class ConversationHeader extends React.Component<Props> {
color, color,
i18n, i18n,
isGroup, isGroup,
isMe,
name, name,
phoneNumber, phoneNumber,
profileName, profileName,
@ -125,6 +141,7 @@ export class ConversationHeader extends React.Component<Props> {
color={color} color={color}
conversationType={isGroup ? 'group' : 'direct'} conversationType={isGroup ? 'group' : 'direct'}
i18n={i18n} i18n={i18n}
noteToSelf={isMe}
name={name} name={name}
phoneNumber={phoneNumber} phoneNumber={phoneNumber}
profileName={profileName} profileName={profileName}

@ -206,8 +206,8 @@
{ {
"rule": "jQuery-wrap(", "rule": "jQuery-wrap(",
"path": "js/models/messages.js", "path": "js/models/messages.js",
"line": " this.send(wrap(promise));", "line": " return this.send(wrap(promise));",
"lineNumber": 820, "lineNumber": 854,
"reasonCategory": "falseMatch", "reasonCategory": "falseMatch",
"updated": "2018-10-05T23:12:28.961Z" "updated": "2018-10-05T23:12:28.961Z"
}, },
@ -215,7 +215,7 @@
"rule": "jQuery-wrap(", "rule": "jQuery-wrap(",
"path": "js/models/messages.js", "path": "js/models/messages.js",
"line": " return wrap(", "line": " return wrap(",
"lineNumber": 1029, "lineNumber": 1091,
"reasonCategory": "falseMatch", "reasonCategory": "falseMatch",
"updated": "2018-10-05T23:12:28.961Z" "updated": "2018-10-05T23:12:28.961Z"
}, },
@ -527,7 +527,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/conversation_search_view.js", "path": "js/views/conversation_search_view.js",
"line": " this.$new_contact = this.$('.new-contact');", "line": " this.$new_contact = this.$('.new-contact');",
"lineNumber": 42, "lineNumber": 40,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -536,7 +536,7 @@
"rule": "jQuery-append(", "rule": "jQuery-append(",
"path": "js/views/conversation_search_view.js", "path": "js/views/conversation_search_view.js",
"line": " this.$el.append(this.typeahead_view.el);", "line": " this.$el.append(this.typeahead_view.el);",
"lineNumber": 59, "lineNumber": 57,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"
@ -545,7 +545,7 @@
"rule": "jQuery-$(", "rule": "jQuery-$(",
"path": "js/views/conversation_search_view.js", "path": "js/views/conversation_search_view.js",
"line": " this.new_contact_view.$('.number').text(i18n('invalidNumberError'));", "line": " this.new_contact_view.$('.number').text(i18n('invalidNumberError'));",
"lineNumber": 111, "lineNumber": 119,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T21:59:32.770Z", "updated": "2018-09-19T21:59:32.770Z",
"reasonDetail": "Protected from arbitrary input" "reasonDetail": "Protected from arbitrary input"
@ -554,7 +554,7 @@
"rule": "jQuery-insertAfter(", "rule": "jQuery-insertAfter(",
"path": "js/views/conversation_search_view.js", "path": "js/views/conversation_search_view.js",
"line": " this.hintView.$el.insertAfter(this.$input);", "line": " this.hintView.$el.insertAfter(this.$input);",
"lineNumber": 147, "lineNumber": 155,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2018-09-19T18:13:29.628Z", "updated": "2018-09-19T18:13:29.628Z",
"reasonDetail": "Interacting with already-existing DOM nodes" "reasonDetail": "Interacting with already-existing DOM nodes"

Loading…
Cancel
Save