Additional logging, new ability to force Conversation 'change' event

This commit is contained in:
Scott Nonnenberg 2021-09-20 11:51:30 -07:00 committed by GitHub
parent e9ef239ff0
commit 250a89d953
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 85 deletions

View file

@ -123,7 +123,7 @@ window.addEventListener('unhandledrejection', rejectionEvent => {
}); });
initLogger( initLogger(
SignalClientLogLevel.Warn, SignalClientLogLevel.Info,
( (
level: unknown, level: unknown,
target: string, target: string,

View file

@ -286,23 +286,28 @@ export class ConversationModel extends window.Backbone
// We clear our cached props whenever we change so that the next call to format() will // We clear our cached props whenever we change so that the next call to format() will
// result in refresh via a getProps() call. See format() below. // result in refresh via a getProps() call. See format() below.
this.on('change', () => { this.on(
const changedKeys = Object.keys(this.changed || {}); 'change',
const isPropsCacheStillValid = Boolean( (_model: MessageModel, options: { force?: boolean } = {}) => {
changedKeys.length && const changedKeys = Object.keys(this.changed || {});
changedKeys.every(key => const isPropsCacheStillValid =
ATTRIBUTES_THAT_DONT_INVALIDATE_PROPS_CACHE.has(key) !options.force &&
) Boolean(
); changedKeys.length &&
if (isPropsCacheStillValid) { changedKeys.every(key =>
return; ATTRIBUTES_THAT_DONT_INVALIDATE_PROPS_CACHE.has(key)
} )
);
if (isPropsCacheStillValid) {
return;
}
if (this.cachedProps) { if (this.cachedProps) {
this.oldCachedProps = this.cachedProps; this.oldCachedProps = this.cachedProps;
}
this.cachedProps = null;
} }
this.cachedProps = null; );
});
// Set `isFetchingUUID` eagerly to avoid UI flicker when opening the // Set `isFetchingUUID` eagerly to avoid UI flicker when opening the
// conversation for the first time. // conversation for the first time.
@ -1039,7 +1044,7 @@ export class ConversationModel extends window.Backbone
); );
this.isFetchingUUID = true; this.isFetchingUUID = true;
this.trigger('change', this); this.trigger('change', this, { force: true });
try { try {
// Attempt to fetch UUID // Attempt to fetch UUID
@ -1051,7 +1056,7 @@ export class ConversationModel extends window.Backbone
} finally { } finally {
// No redux update here // No redux update here
this.isFetchingUUID = false; this.isFetchingUUID = false;
this.trigger('change', this); this.trigger('change', this, { force: true });
log.info( log.info(
`Done fetching uuid for a sms-only conversation ${this.idForLogging()}` `Done fetching uuid for a sms-only conversation ${this.idForLogging()}`
@ -2449,7 +2454,7 @@ export class ConversationModel extends window.Backbone
// If the verified state of a member changes, our aggregate state changes. // If the verified state of a member changes, our aggregate state changes.
// We trigger both events to replicate the behavior of window.Backbone.Model.set() // We trigger both events to replicate the behavior of window.Backbone.Model.set()
this.trigger('change:verified', this); this.trigger('change:verified', this);
this.trigger('change', this); this.trigger('change', this, { force: true });
} }
async toggleVerified(): Promise<unknown> { async toggleVerified(): Promise<unknown> {
@ -2859,10 +2864,7 @@ export class ConversationModel extends window.Backbone
); );
} }
async onReadMessage( async onReadMessage(message: MessageModel, readAt?: number): Promise<void> {
message: MessageModel,
readAt?: number
): Promise<WhatIsThis> {
// We mark as read everything older than this message - to clean up old stuff // 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 // still marked unread in the database. If the user generally doesn't read in
// the desktop app, so the desktop app only gets read syncs, we can very // the desktop app, so the desktop app only gets read syncs, we can very
@ -3397,7 +3399,7 @@ export class ConversationModel extends window.Backbone
targetAuthorUuid: string; targetAuthorUuid: string;
targetTimestamp: number; targetTimestamp: number;
} }
): Promise<WhatIsThis> { ): Promise<void> {
const { messageId } = target; const { messageId } = target;
const timestamp = Date.now(); const timestamp = Date.now();
const outgoingReaction = { ...reaction, ...target }; const outgoingReaction = { ...reaction, ...target };
@ -4950,13 +4952,13 @@ export class ConversationModel extends window.Backbone
); );
if (!record) { if (!record) {
// User was not previously typing before. State change! // User was not previously typing before. State change!
this.trigger('change', this); this.trigger('change', this, { force: true });
} }
} else { } else {
delete this.contactTypingTimers[typingToken]; delete this.contactTypingTimers[typingToken];
if (record) { if (record) {
// User was previously typing, and is no longer. State change! // User was previously typing, and is no longer. State change!
this.trigger('change', this); this.trigger('change', this, { force: true });
} }
} }
} }
@ -4970,7 +4972,7 @@ export class ConversationModel extends window.Backbone
delete this.contactTypingTimers[typingToken]; delete this.contactTypingTimers[typingToken];
// User was previously typing, but timed out or we received message. State change! // User was previously typing, but timed out or we received message. State change!
this.trigger('change', this); this.trigger('change', this, { force: true });
} }
} }
@ -5061,7 +5063,8 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
this.eraseLookups(); this.eraseLookups();
this.on( this.on(
'idUpdated', 'idUpdated',
(model: WhatIsThis, idProp: string, oldValue: WhatIsThis) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any
(model: ConversationModel, idProp: string, oldValue: any) => {
if (oldValue) { if (oldValue) {
if (idProp === 'e164') { if (idProp === 'e164') {
delete this._byE164[oldValue]; delete this._byE164[oldValue];
@ -5073,14 +5076,17 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
delete this._byGroupId[oldValue]; delete this._byGroupId[oldValue];
} }
} }
if (model.get('e164')) { const e164 = model.get('e164');
this._byE164[model.get('e164')] = model; if (e164) {
this._byE164[e164] = model;
} }
if (model.get('uuid')) { const uuid = model.get('uuid');
this._byUuid[model.get('uuid')] = model; if (uuid) {
this._byUuid[uuid] = model;
} }
if (model.get('groupId')) { const groupId = model.get('groupId');
this._byGroupId[model.get('groupId')] = model; if (groupId) {
this._byGroupId[groupId] = model;
} }
} }
); );
@ -5185,51 +5191,6 @@ window.Whisper.ConversationCollection = window.Backbone.Collection.extend({
}, },
}); });
// This is a wrapper model used to display group members in the member list view, within
// the world of backbone, but layering another bit of group-specific data top of base
// conversation data.
window.Whisper.GroupMemberConversation = window.Backbone.Model.extend({
initialize(attributes: { conversation: boolean; isAdmin: boolean }) {
const { conversation, isAdmin } = attributes;
if (!conversation) {
throw new Error(
'GroupMemberConversation.initialize: conversation required!'
);
}
if (!window._.isBoolean(isAdmin)) {
throw new Error('GroupMemberConversation.initialize: isAdmin required!');
}
// If our underlying conversation changes, we change too
this.listenTo(conversation, 'change', () => {
this.trigger('change', this);
});
this.conversation = conversation;
this.isAdmin = isAdmin;
},
format() {
return {
...this.conversation.format(),
isAdmin: this.isAdmin,
};
},
get(...params: Array<string>) {
return this.conversation.get(...params);
},
getTitle() {
return this.conversation.getTitle();
},
isMe() {
return isMe(this.conversation.attributes);
},
});
type SortableByTitle = { type SortableByTitle = {
getTitle: () => string; getTitle: () => string;
}; };

View file

@ -1320,7 +1320,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
async send( async send(
promise: Promise<CallbackResultType | void | null>, promise: Promise<CallbackResultType | void | null>,
saveErrors?: (errors: Array<Error>) => void saveErrors?: (errors: Array<Error>) => void
): Promise<void | Array<void>> { ): Promise<void> {
const updateLeftPane = const updateLeftPane =
this.getConversation()?.debouncedUpdateLastMessage || noop; this.getConversation()?.debouncedUpdateLastMessage || noop;

View file

@ -1164,8 +1164,8 @@ export default class MessageReceiver
if (envelope.serverTimestamp > certificate.expiration()) { if (envelope.serverTimestamp > certificate.expiration()) {
throw new Error( throw new Error(
`MessageReceiver.validateUnsealedEnvelope: ' + 'MessageReceiver.validateUnsealedEnvelope: ' +
'Sender certificate is expired for envelope ${logId}` `Sender certificate is expired for envelope ${logId}`
); );
} }

View file

@ -71,6 +71,8 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
); );
} }
await archiveSessionOnMatch(retryRequest);
if (isOlderThan(sentAt, retryRespondMaxAge)) { if (isOlderThan(sentAt, retryRespondMaxAge)) {
log.info( log.info(
`onRetryRequest/${logId}: Message is too old, refusing to send again.` `onRetryRequest/${logId}: Message is too old, refusing to send again.`
@ -92,7 +94,6 @@ export async function onRetryRequest(event: RetryRequestEvent): Promise<void> {
} }
log.info(`onRetryRequest/${logId}: Resending message`); log.info(`onRetryRequest/${logId}: Resending message`);
await archiveSessionOnMatch(retryRequest);
const { contentHint, messageIds, proto, timestamp } = sentProto; const { contentHint, messageIds, proto, timestamp } = sentProto;
@ -206,8 +207,6 @@ async function sendDistributionMessageOrNullMessage(
let sentDistributionMessage = false; let sentDistributionMessage = false;
log.info(`sendDistributionMessageOrNullMessage/${logId}: Starting...`); log.info(`sendDistributionMessageOrNullMessage/${logId}: Starting...`);
await archiveSessionOnMatch(options);
const conversation = window.ConversationController.getOrCreate( const conversation = window.ConversationController.getOrCreate(
requesterUuid, requesterUuid,
'private' 'private'