diff --git a/js/models/conversations.js b/js/models/conversations.js index be986d0189..f8db5b6a1b 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -169,11 +169,11 @@ this.typingPauseTimer = null; // Keep props ready - const generateProps = () => { + this.generateProps = () => { this.cachedProps = this.getProps(); }; - this.on('change', generateProps); - generateProps(); + this.on('change', this.generateProps); + this.generateProps(); }, isMe() { @@ -444,6 +444,14 @@ return this.cachedProps; }, getProps() { + // This is to prevent race conditions on startup; Conversation models are created + // but the full ConversationController.load() sequence isn't complete. So, we + // don't cache props on create, but we do later when load() calls generateProps() + // for us. + if (!window.ConversationController.isFetchComplete()) { + return null; + } + const color = this.getColor(); const typingValues = _.values(this.contactTypingTimers || {}); diff --git a/js/models/messages.js b/js/models/messages.js index c8b1b00f30..d6c26a6ab1 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -664,10 +664,7 @@ return ConversationController.get(identifier); }, getConversation() { - // This needs to be an unsafe call, because this method is called during - // initial module setup. We may be in the middle of the initial fetch to - // the database. - return ConversationController.getUnsafe(this.get('conversationId')); + return ConversationController.get(this.get('conversationId')); }, createNonBreakingLastSeparator(text) { if (!text) { diff --git a/ts/ConversationController.ts b/ts/ConversationController.ts index f2e6e166a4..5b263932d1 100644 --- a/ts/ConversationController.ts +++ b/ts/ConversationController.ts @@ -91,10 +91,6 @@ export class ConversationController { // This function takes null just fine. Backbone typings are too restrictive. return this._conversations.get(id as string); } - // Needed for some model setup which happens during the initial fetch() call below - getUnsafe(id: string) { - return this._conversations.get(id); - } dangerouslyCreateAndAdd(attributes: Partial) { return this._conversations.add(attributes); } @@ -617,6 +613,9 @@ export class ConversationController { this._initialFetchComplete = false; this._conversations.reset([]); } + isFetchComplete() { + return this._initialFetchComplete; + } async load() { window.log.info('ConversationController: starting initial fetch'); @@ -636,6 +635,12 @@ export class ConversationController { await Promise.all( this._conversations.map(async conversation => { + // This call is important to allow Conversation models not to generate their + // cached props on initial construction if we're in the middle of the load + // from the database. Then we come back to the models when it is safe and + // generate those props. + conversation.generateProps(); + if (!conversation.get('lastMessage')) { await conversation.updateLastMessage(); } diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts index 47cfcbbd85..a4127ad64b 100644 --- a/ts/model-types.d.ts +++ b/ts/model-types.d.ts @@ -85,6 +85,7 @@ declare class ConversationModelType extends Backbone.Model< cleanup(): Promise; disableProfileSharing(): void; dropProfileKey(): Promise; + generateProps(): void; getAccepted(): boolean; getAvatarPath(): string | undefined; getColor(): ColorType | undefined; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index a28c4de060..b4fc2352bb 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -203,11 +203,27 @@ "reasonCategory": "usageTrusted", "updated": "2020-03-25T15:45:04.024Z" }, + { + "rule": "jQuery-load(", + "path": "js/models/conversations.js", + "line": " // but the full ConversationController.load() sequence isn't complete. So, we", + "lineNumber": 448, + "reasonCategory": "exampleCode", + "updated": "2020-08-11T21:28:50.868Z" + }, + { + "rule": "jQuery-load(", + "path": "js/models/conversations.js", + "line": " // don't cache props on create, but we do later when load() calls generateProps()", + "lineNumber": 449, + "reasonCategory": "exampleCode", + "updated": "2020-08-11T21:28:50.868Z" + }, { "rule": "jQuery-wrap(", "path": "js/models/conversations.js", "line": " await wrap(", - "lineNumber": 665, + "lineNumber": 673, "reasonCategory": "falseMatch", "updated": "2020-06-09T20:26:46.515Z" }, @@ -12067,4 +12083,4 @@ "reasonCategory": "falseMatch", "updated": "2020-04-05T23:45:16.746Z" } -] \ No newline at end of file +]